Commit 4cc52f12 by Randy Döring Committed by Arun Babu Neelicattu

provider: merge duplicate dependencies by marker (avoid unnecessary solver failures)

parent ec9c0565
......@@ -7,11 +7,13 @@ import tempfile
import time
import urllib.parse
from collections import defaultdict
from contextlib import contextmanager
from pathlib import Path
from tempfile import mkdtemp
from typing import TYPE_CHECKING
from typing import Any
from typing import Iterable
from typing import Iterator
from cleo.ui.progress_indicator import ProgressIndicator
......@@ -42,6 +44,8 @@ if TYPE_CHECKING:
from poetry.core.packages.package import Package
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.semver.version_constraint import VersionConstraint
from poetry.core.version.markers import BaseMarker
from poetry.repositories import Pool
from poetry.utils.env import Env
......@@ -525,15 +529,12 @@ class Provider:
self.debug(f"<debug>Duplicate dependencies for {dep_name}</debug>")
# Regrouping by constraint
by_constraint: dict[str, list[Dependency]] = {}
for dep in deps:
if dep.constraint not in by_constraint:
by_constraint[dep.constraint] = []
deps = self._merge_dependencies_by_marker(deps)
# Merging dependencies by constraint
by_constraint: dict[VersionConstraint, list[Dependency]] = defaultdict(list)
for dep in deps:
by_constraint[dep.constraint].append(dep)
# We merge by constraint
for constraint, _deps in by_constraint.items():
new_markers = []
for dep in _deps:
......@@ -810,3 +811,29 @@ class Provider:
yield
self._in_progress = False
def _merge_dependencies_by_marker(
self, dependencies: Iterable[Dependency]
) -> list[Dependency]:
by_marker: dict[BaseMarker, list[Dependency]] = defaultdict(list)
for dep in dependencies:
by_marker[dep.marker].append(dep)
deps = []
for _deps in by_marker.values():
if len(_deps) == 1:
deps.extend(_deps)
else:
new_constraint = _deps[0].constraint
for dep in _deps[1:]:
new_constraint = new_constraint.intersect(dep.constraint)
if new_constraint.is_empty():
# leave dependencies as-is so the resolver will pickup
# the conflict and display a proper error.
deps.extend(_deps)
else:
self.debug(
f"<debug>Merging constraints for {_deps[0].name} for"
f" marker {_deps[0].marker}</debug>"
)
deps.append(_deps[0].with_constraint(new_constraint))
return deps
......@@ -1300,6 +1300,43 @@ So, because no versions of a match !=1.0
assert str(e.value) == expected
def test_solver_duplicate_dependencies_different_constraints_merge_by_marker(
solver: Solver, repo: Repository, package: Package
):
package.add_dependency(Factory.create_dependency("A", "*"))
package_a = get_package("A", "1.0")
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^1.0", "python": "<3.4"})
)
package_a.add_dependency(
Factory.create_dependency("B", {"version": "^2.0", "python": ">=3.4"})
)
package_a.add_dependency(
Factory.create_dependency("B", {"version": "!=1.1", "python": "<3.4"})
)
package_b10 = get_package("B", "1.0")
package_b11 = get_package("B", "1.1")
package_b20 = get_package("B", "2.0")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b11)
repo.add_package(package_b20)
transaction = solver.solve()
check_solver_result(
transaction,
[
{"job": "install", "package": package_b10},
{"job": "install", "package": package_b20},
{"job": "install", "package": package_a},
],
)
def test_solver_duplicate_dependencies_different_constraints_merge_no_markers(
solver: Solver, repo: Repository, package: Package
):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment