Skip to content

Commit 38e425f

Browse files
authored
Merge pull request #797 from maresb/v2-v3-lockfile-consistency
Improve compatibility between lockfiles produced by v2 and v3
2 parents ee628bd + 97ae574 commit 38e425f

21 files changed

+12902
-222
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ repos:
1616
exclude: ^conda_lock/_vendor/conda/_vendor/appdirs\.py$
1717

1818
- repo: https://github.com/astral-sh/ruff-pre-commit
19-
rev: v0.11.9
19+
rev: v0.11.13
2020
hooks:
2121
- id: ruff
2222
args: [--fix, --exit-non-zero-on-fix]
2323
- id: ruff-format
2424

2525
- repo: https://github.com/pre-commit/mirrors-mypy
26-
rev: v1.15.0
26+
rev: v1.16.0
2727
hooks:
2828
- id: mypy
2929
additional_dependencies:

conda_lock/conda_lock.py

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,15 @@
5050
write_file,
5151
)
5252
from conda_lock.conda_solver import solve_conda
53-
from conda_lock.content_hash_types import EmptyDict, HashableFakePackage, SubdirMetadata
53+
from conda_lock.content_hash import (
54+
backwards_compatible_content_hashes,
55+
compute_content_hashes,
56+
)
57+
from conda_lock.content_hash_types import (
58+
EmptyDict,
59+
HashableVirtualPackage,
60+
SubdirMetadata,
61+
)
5462
from conda_lock.errors import MissingEnvVarError, PlatformValidationError
5563
from conda_lock.export_lock_spec import EditableDependency, render_pixi_toml
5664
from conda_lock.invoke_conda import (
@@ -253,6 +261,22 @@ def fn_to_dist_name(fn: str) -> str:
253261
return fn
254262

255263

264+
def _compute_filtered_categories(
265+
include_dev_dependencies: bool, extras: Optional[AbstractSet[str]]
266+
) -> AbstractSet[str]:
267+
"""Compute the selected subset of categories when filtering.
268+
269+
Includes `main`, `dev` unless disabled by the flag, and any explicitly specified
270+
extras.
271+
"""
272+
filtered_categories = {"main"}
273+
if include_dev_dependencies:
274+
filtered_categories.add("dev")
275+
if extras is not None:
276+
filtered_categories.update(extras)
277+
return filtered_categories
278+
279+
256280
def make_lock_files( # noqa: C901
257281
*,
258282
conda: PathLike,
@@ -315,18 +339,17 @@ def make_lock_files( # noqa: C901
315339
metadata_yamls:
316340
YAML or JSON file(s) containing structured metadata to add to metadata section of the lockfile.
317341
"""
318-
319342
# Compute lock specification
320-
required_categories = {"main"}
321-
if include_dev_dependencies:
322-
required_categories.add("dev")
323-
if extras is not None:
324-
required_categories.update(extras)
343+
filtered_categories: Optional[AbstractSet[str]] = None
344+
if filter_categories:
345+
filtered_categories = _compute_filtered_categories(
346+
include_dev_dependencies=include_dev_dependencies, extras=extras
347+
)
325348
lock_spec = make_lock_spec(
326349
src_files=src_files,
327350
channel_overrides=channel_overrides,
328351
platform_overrides=platform_overrides,
329-
required_categories=required_categories if filter_categories else None,
352+
filtered_categories=filtered_categories,
330353
mapping_url=mapping_url,
331354
)
332355

@@ -348,11 +371,21 @@ def make_lock_files( # noqa: C901
348371
virtual_package_repo = virtual_package_repo_from_specification(
349372
virtual_package_spec
350373
)
374+
if with_cuda is not None:
375+
if with_cuda == "":
376+
logger.warning(
377+
"Ignoring --without-cuda in favor of --virtual-package-spec"
378+
)
379+
else:
380+
logger.warning(
381+
f"Ignoring --with-cuda {with_cuda} in favor of "
382+
"--virtual-package-spec"
383+
)
351384
cuda_specified = True
352385
else:
353386
if with_cuda is None:
354387
cuda_specified = False
355-
with_cuda = "11.4"
388+
with_cuda = "default"
356389
else:
357390
cuda_specified = True
358391
virtual_package_repo = default_virtual_package_repodata(cuda_version=with_cuda)
@@ -373,10 +406,10 @@ def make_lock_files( # noqa: C901
373406
update
374407
or platform not in platforms_already_locked
375408
or not check_input_hash
376-
or lock_spec.content_hash_for_platform(
377-
platform, virtual_package_repo
409+
or original_lock_content.metadata.content_hash[platform]
410+
not in backwards_compatible_content_hashes(
411+
lock_spec, virtual_package_repo, platform
378412
)
379-
!= original_lock_content.metadata.content_hash[platform]
380413
):
381414
platforms_to_lock.append(platform)
382415
if platform in platforms_already_locked:
@@ -773,7 +806,7 @@ def _solve_for_arch(
773806
if "python" not in conda_deps:
774807
raise ValueError("Got pip specs without Python")
775808

776-
platform_virtual_packages: Optional[Dict[str, HashableFakePackage]]
809+
platform_virtual_packages: Optional[Dict[str, HashableVirtualPackage]]
777810
if not virtual_package_repo:
778811
# Type checking seems to prove that this is unreachable.
779812
platform_virtual_packages = None
@@ -912,12 +945,12 @@ def create_lockfile_from_spec(
912945
inputs_metadata = None
913946

914947
custom_metadata = get_custom_metadata(metadata_yamls=metadata_yamls)
915-
content_hash = spec.content_hash(virtual_package_repo)
948+
content_hashes = compute_content_hashes(spec, virtual_package_repo)
916949

917950
return Lockfile(
918951
package=[locked[k] for k in locked],
919952
metadata=LockMeta(
920-
content_hash=content_hash,
953+
content_hash=content_hashes,
921954
channels=[c for c in spec.channels],
922955
platforms=spec.platforms,
923956
sources=list(meta_sources.keys()),
@@ -1754,7 +1787,7 @@ def render(
17541787
@click.option(
17551788
"-k",
17561789
"--kind",
1757-
type=click.Choice(["pixi.toml"]),
1790+
type=click.Choice(["pixi.toml", "raw"]),
17581791
multiple=True,
17591792
help="Kind of lock specification to generate. Must be 'pixi.toml'.",
17601793
)
@@ -1885,7 +1918,7 @@ def render_lock_spec( # noqa: C901
18851918
channel_overrides: Sequence[str],
18861919
dev_dependencies: bool,
18871920
files: Sequence[PathLike],
1888-
kind: Sequence[Literal["pixi.toml"]],
1921+
kind: Sequence[Literal["pixi.toml", "raw"]],
18891922
filename_template: Optional[str],
18901923
lockfile: Optional[PathLike],
18911924
strip_auth: bool,
@@ -1906,9 +1939,12 @@ def render_lock_spec( # noqa: C901
19061939
) -> None:
19071940
"""Combine source files into a single lock specification"""
19081941
kinds = set(kind)
1909-
if kinds != {"pixi.toml"}:
1942+
if len(kinds) == 0:
1943+
raise ValueError("No kind specified. Add `--kind=pixi.toml` or `--kind=raw`.")
1944+
if not kinds <= {"pixi.toml", "raw"}:
19101945
raise NotImplementedError(
1911-
"Only 'pixi.toml' is supported at the moment. Add `--kind=pixi.toml`."
1946+
"Only 'pixi.toml' and 'raw' are supported at the moment. "
1947+
"Add `--kind=pixi.toml` or `--kind=raw`."
19121948
)
19131949
if pixi_project_name is not None and "pixi.toml" not in kinds:
19141950
raise ValueError("The --pixi-project-name option is only valid for pixi.toml")
@@ -2018,7 +2054,7 @@ def render_lock_spec( # noqa: C901
20182054
def do_render_lockspec(
20192055
src_files: List[pathlib.Path],
20202056
*,
2021-
kinds: AbstractSet[Literal["pixi.toml"]],
2057+
kinds: AbstractSet[Literal["pixi.toml", "raw"]],
20222058
stdout: bool,
20232059
platform_overrides: Optional[Sequence[str]] = None,
20242060
channel_overrides: Optional[Sequence[str]] = None,
@@ -2034,18 +2070,27 @@ def do_render_lockspec(
20342070
if len(src_files) == 0:
20352071
src_files = handle_no_specified_source_files(lockfile_path)
20362072

2037-
required_categories = {"main"}
2038-
if include_dev_dependencies:
2039-
required_categories.add("dev")
2040-
if extras is not None:
2041-
required_categories.update(extras)
2073+
filtered_categories: Optional[AbstractSet[str]] = None
2074+
if filter_categories:
2075+
filtered_categories = _compute_filtered_categories(
2076+
include_dev_dependencies=include_dev_dependencies, extras=extras
2077+
)
20422078
lock_spec = make_lock_spec(
20432079
src_files=src_files,
20442080
channel_overrides=channel_overrides,
20452081
platform_overrides=platform_overrides,
2046-
required_categories=required_categories if filter_categories else None,
2082+
filtered_categories=filtered_categories,
20472083
mapping_url=mapping_url,
20482084
)
2085+
if "raw" in kinds:
2086+
if stdout:
2087+
warn(
2088+
"Raw lockspec is not intended to be stable "
2089+
"between any conda-lock versions."
2090+
)
2091+
print(lock_spec.model_dump_json(indent=2))
2092+
else:
2093+
raise NotImplementedError("Only stdout is supported at the moment.")
20492094
if "pixi.toml" in kinds:
20502095
pixi_toml = render_pixi_toml(
20512096
lock_spec=lock_spec,

conda_lock/conda_solver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def _to_match_spec(
101101
if conda_channel:
102102
kwargs["channel"] = conda_channel
103103

104-
ms = MatchSpec(**kwargs)
104+
ms = MatchSpec(**kwargs) # pyright: ignore[reportArgumentType]
105105
# Since MatchSpec doesn't round trip to the cli well
106106
if conda_channel:
107107
# this will return "channel_name::package_name"
@@ -178,7 +178,7 @@ def solve_conda(
178178
for action in dry_run_install["actions"]["FETCH"]:
179179
dependencies = {}
180180
for dep in action.get("depends") or []:
181-
matchspec = MatchSpec(dep)
181+
matchspec = MatchSpec(dep) # pyright: ignore[reportArgumentType]
182182
name = matchspec.name
183183
version = (
184184
matchspec.version.spec_str if matchspec.version is not None else ""

0 commit comments

Comments
 (0)