Skip to content

Commit

Permalink
Update and Add Tests for Merging Versions
Browse files Browse the repository at this point in the history
  • Loading branch information
srilman committed Dec 18, 2022
1 parent 0b5e259 commit a952f3a
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 9 deletions.
28 changes: 21 additions & 7 deletions conda_lock/src_parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,29 +540,43 @@ def aggregate_lock_specs(
[lock_spec.dependencies for lock_spec in lock_specs]
):
key = (dep.manager, dep.name)
new_dep = dep.copy()
if key in unique_deps:
prev_dep = unique_deps[key]

# Override existing, but merge selectors
previous_selectors = prev_dep.selectors
previous_selectors |= dep.selectors
dep.selectors = previous_selectors
new_dep.selectors = previous_selectors

# If bold old and new are VersionedDependency, combine versions
# If bold old and new are VersionedDependency, combine version strings together
# If there are conflicting versions, they will be handled by the solver
if isinstance(prev_dep, VersionedDependency) and isinstance(
dep, VersionedDependency
):
assert isinstance(new_dep, VersionedDependency)

# MatchSpecs are expressions in DNF form: `|` is OR and `,` is AND
prev_versions = [
set(sec.split(",")) for sec in prev_dep.version.split("|")
set(sec.split(","))
for sec in prev_dep.version.split("|")
if sec != ""
]
new_versions = [
set(sec.split(",")) for sec in dep.version.split("|") if sec != ""
]
new_versions = [set(sec.split(",")) for sec in dep.version.split("|")]
# To AND two DNF expressions, we essentially perform the distributive law
# ORing every pair of terms in the individual expressions
# Ex: (AB | CD) (AC | BD) = ABAC | ABBD | CDAC | CDBC = ABC | ABD | ACD | BCD
cross_versions = [
prev | new for prev, new in product(prev_versions, new_versions)
]
final_versions = set(",".join(subset) for subset in cross_versions)
dep.version = "|".join(final_versions)
final_versions = set(
",".join(sorted(subset, reverse=True)) for subset in cross_versions
)
new_dep.version = "|".join(sorted(final_versions, reverse=True))

unique_deps[key] = dep
unique_deps[key] = new_dep

dependencies = list(unique_deps.values())
try:
Expand Down
24 changes: 22 additions & 2 deletions tests/test_conda_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ def test_aggregate_lock_specs_multiple_platforms():
assert actual.content_hash() == expected.content_hash()


def test_aggregate_lock_specs_override_version():
def test_aggregate_lock_specs_combine_version():
base_spec = LockSpecification(
dependencies=[_make_spec("package", "=1.0")],
channels=[Channel.from_string("conda-forge")],
Expand All @@ -1139,8 +1139,28 @@ def test_aggregate_lock_specs_override_version():
)

agg_spec = aggregate_lock_specs([base_spec, override_spec])
assert agg_spec.dependencies == [_make_spec("package", "=2.0,=1.0")]

assert agg_spec.dependencies == override_spec.dependencies

def test_aggregate_lock_specs_with_union_version():
base_spec = LockSpecification(
dependencies=[_make_spec("package", "=1.0|>2")],
channels=[Channel.from_string("conda-forge")],
platforms=["linux-64"],
sources=[Path("base.yml")],
)

override_spec = LockSpecification(
dependencies=[_make_spec("package", "=2.0,<3.0.0")],
channels=[Channel.from_string("internal"), Channel.from_string("conda-forge")],
platforms=["linux-64"],
sources=[Path("override.yml")],
)

agg_spec = aggregate_lock_specs([base_spec, override_spec])
assert agg_spec.dependencies == [
_make_spec("package", ">2,=2.0,<3.0.0|=2.0,=1.0,<3.0.0")
]


def test_aggregate_lock_specs_invalid_channels():
Expand Down

0 comments on commit a952f3a

Please sign in to comment.