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 ...@@ -7,11 +7,13 @@ import tempfile
import time import time
import urllib.parse import urllib.parse
from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from tempfile import mkdtemp from tempfile import mkdtemp
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any from typing import Any
from typing import Iterable
from typing import Iterator from typing import Iterator
from cleo.ui.progress_indicator import ProgressIndicator from cleo.ui.progress_indicator import ProgressIndicator
...@@ -42,6 +44,8 @@ if TYPE_CHECKING: ...@@ -42,6 +44,8 @@ if TYPE_CHECKING:
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.url_dependency import URLDependency from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency 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.repositories import Pool
from poetry.utils.env import Env from poetry.utils.env import Env
...@@ -525,15 +529,12 @@ class Provider: ...@@ -525,15 +529,12 @@ class Provider:
self.debug(f"<debug>Duplicate dependencies for {dep_name}</debug>") self.debug(f"<debug>Duplicate dependencies for {dep_name}</debug>")
# Regrouping by constraint deps = self._merge_dependencies_by_marker(deps)
by_constraint: dict[str, list[Dependency]] = {}
for dep in deps:
if dep.constraint not in by_constraint:
by_constraint[dep.constraint] = []
# Merging dependencies by constraint
by_constraint: dict[VersionConstraint, list[Dependency]] = defaultdict(list)
for dep in deps:
by_constraint[dep.constraint].append(dep) by_constraint[dep.constraint].append(dep)
# We merge by constraint
for constraint, _deps in by_constraint.items(): for constraint, _deps in by_constraint.items():
new_markers = [] new_markers = []
for dep in _deps: for dep in _deps:
...@@ -810,3 +811,29 @@ class Provider: ...@@ -810,3 +811,29 @@ class Provider:
yield yield
self._in_progress = False 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 ...@@ -1300,6 +1300,43 @@ So, because no versions of a match !=1.0
assert str(e.value) == expected 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( def test_solver_duplicate_dependencies_different_constraints_merge_no_markers(
solver: Solver, repo: Repository, package: Package 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