Commit 28ddcd8b by Randy Döring Committed by Bjorn Neergaard

provider: discard any marker dependencies if the resulting marker after merging…

provider: discard any marker dependencies if the resulting marker after merging is empty, not compatible with the project's python constraint or not compatible with the set environment
parent e9730ddc
...@@ -740,7 +740,7 @@ class Provider: ...@@ -740,7 +740,7 @@ class Provider:
f"<warning>Different requirements found for {warnings}.</warning>" f"<warning>Different requirements found for {warnings}.</warning>"
) )
self._handle_any_marker_dependencies(deps) deps = self._handle_any_marker_dependencies(deps)
overrides = [] overrides = []
overrides_marker_intersection: BaseMarker = AnyMarker() overrides_marker_intersection: BaseMarker = AnyMarker()
...@@ -974,7 +974,9 @@ class Provider: ...@@ -974,7 +974,9 @@ class Provider:
deps.append(_deps[0].with_constraint(new_constraint)) deps.append(_deps[0].with_constraint(new_constraint))
return deps return deps
def _handle_any_marker_dependencies(self, dependencies: list[Dependency]) -> None: def _handle_any_marker_dependencies(
self, dependencies: list[Dependency]
) -> list[Dependency]:
""" """
We need to check if one of the duplicate dependencies We need to check if one of the duplicate dependencies
has no markers. If there is one, we need to change its has no markers. If there is one, we need to change its
...@@ -997,32 +999,42 @@ class Provider: ...@@ -997,32 +999,42 @@ class Provider:
any_markers_dependencies = [d for d in dependencies if d.marker.is_any()] any_markers_dependencies = [d for d in dependencies if d.marker.is_any()]
other_markers_dependencies = [d for d in dependencies if not d.marker.is_any()] other_markers_dependencies = [d for d in dependencies if not d.marker.is_any()]
for dep_any in any_markers_dependencies:
for dep_other in other_markers_dependencies:
dep_other.constraint = dep_other.constraint.intersect(
dep_any.constraint
)
marker = other_markers_dependencies[0].marker marker = other_markers_dependencies[0].marker
for other_dep in other_markers_dependencies[1:]: for other_dep in other_markers_dependencies[1:]:
marker = marker.union(other_dep.marker) marker = marker.union(other_dep.marker)
inverted_marker = marker.invert() inverted_marker = marker.invert()
if any_markers_dependencies: if (
for dep_any in any_markers_dependencies: not inverted_marker.is_empty()
dep_any.marker = inverted_marker and self._python_constraint.allows_any(
for dep_other in other_markers_dependencies:
dep_other.constraint = dep_other.constraint.intersect(
dep_any.constraint
)
elif not inverted_marker.is_empty() and self._python_constraint.allows_any(
get_python_constraint_from_marker(inverted_marker) get_python_constraint_from_marker(inverted_marker)
)
and (not self._env or inverted_marker.validate(self._env.marker_env))
): ):
# if there is no any marker dependency if any_markers_dependencies:
# and the inverted marker is not empty, for dep_any in any_markers_dependencies:
# a dependency with the inverted union of all markers is required dep_any.marker = inverted_marker
# in order to not miss other dependencies later, for instance: else:
# - foo (1.0) ; python == 3.7 # If there is no any marker dependency
# - foo (2.0) ; python == 3.8 # and the inverted marker is not empty,
# - bar (2.0) ; python == 3.8 # a dependency with the inverted union of all markers is required
# - bar (3.0) ; python == 3.9 # in order to not miss other dependencies later, for instance:
# # - foo (1.0) ; python == 3.7
# the last dependency would be missed without this, # - foo (2.0) ; python == 3.8
# because the intersection with both foo dependencies is empty # - bar (2.0) ; python == 3.8
inverted_marker_dep = dependencies[0].with_constraint(EmptyConstraint()) # - bar (3.0) ; python == 3.9
inverted_marker_dep.marker = inverted_marker #
dependencies.append(inverted_marker_dep) # the last dependency would be missed without this,
# because the intersection with both foo dependencies is empty.
inverted_marker_dep = dependencies[0].with_constraint(EmptyConstraint())
inverted_marker_dep.marker = inverted_marker
dependencies.append(inverted_marker_dep)
else:
dependencies = other_markers_dependencies
return dependencies
...@@ -1480,6 +1480,143 @@ def test_solver_duplicate_dependencies_different_constraints_merge_no_markers( ...@@ -1480,6 +1480,143 @@ def test_solver_duplicate_dependencies_different_constraints_merge_no_markers(
) )
def test_solver_duplicate_dependencies_different_constraints_discard_no_markers1(
solver: Solver, repo: Repository, package: ProjectPackage
) -> None:
"""
Initial dependencies:
A (>=1.0)
A (<1.2) ; python >= 3.10
A (<1.1) ; python < 3.10
Merged dependencies:
A (>=1.0) ; <empty>
A (>=1.0,<1.2) ; python >= 3.10
A (>=1.0,<1.1) ; python < 3.10
The dependency with an empty marker has to be ignored.
"""
package.add_dependency(Factory.create_dependency("A", ">=1.0"))
package.add_dependency(
Factory.create_dependency("A", {"version": "<1.2", "python": ">=3.10"})
)
package.add_dependency(
Factory.create_dependency("A", {"version": "<1.1", "python": "<3.10"})
)
package.add_dependency(Factory.create_dependency("B", "*"))
package_a10 = get_package("A", "1.0")
package_a11 = get_package("A", "1.1")
package_a12 = get_package("A", "1.2")
package_b = get_package("B", "1.0")
package_b.add_dependency(Factory.create_dependency("A", "*"))
repo.add_package(package_a10)
repo.add_package(package_a11)
repo.add_package(package_a12)
repo.add_package(package_b)
transaction = solver.solve()
check_solver_result(
transaction,
[
# only a10 and a11, not a12
{"job": "install", "package": package_a10},
{"job": "install", "package": package_a11},
{"job": "install", "package": package_b},
],
)
def test_solver_duplicate_dependencies_different_constraints_discard_no_markers2(
solver: Solver, repo: Repository, package: ProjectPackage
) -> None:
"""
Initial dependencies:
A (>=1.0)
A (<1.2) ; python == 3.10
Merged dependencies:
A (>=1.0) ; python != 3.10
A (>=1.0,<1.2) ; python == 3.10
The first dependency has to be ignored
because it is not compatible with the project's python constraint.
"""
set_package_python_versions(solver.provider, "~3.10")
package.add_dependency(Factory.create_dependency("A", ">=1.0"))
package.add_dependency(
Factory.create_dependency("A", {"version": "<1.2", "python": "3.10"})
)
package.add_dependency(Factory.create_dependency("B", "*"))
package_a10 = get_package("A", "1.0")
package_a11 = get_package("A", "1.1")
package_a12 = get_package("A", "1.2")
package_b = get_package("B", "1.0")
package_b.add_dependency(Factory.create_dependency("A", "*"))
repo.add_package(package_a10)
repo.add_package(package_a11)
repo.add_package(package_a12)
repo.add_package(package_b)
transaction = solver.solve()
check_solver_result(
transaction,
[
{"job": "install", "package": package_a11}, # only a11, not a12
{"job": "install", "package": package_b},
],
)
def test_solver_duplicate_dependencies_different_constraints_discard_no_markers3(
solver: Solver, repo: Repository, package: ProjectPackage
) -> None:
"""
Initial dependencies:
A (>=1.0)
A (<1.2) ; python == 3.10
Merged dependencies:
A (>=1.0) ; python != 3.10
A (>=1.0,<1.2) ; python == 3.10
The first dependency has to be ignored
because it is not compatible with the current environment.
"""
package.add_dependency(Factory.create_dependency("A", ">=1.0"))
package.add_dependency(
Factory.create_dependency("A", {"version": "<1.2", "python": "3.10"})
)
package.add_dependency(Factory.create_dependency("B", "*"))
package_a10 = get_package("A", "1.0")
package_a11 = get_package("A", "1.1")
package_a12 = get_package("A", "1.2")
package_b = get_package("B", "1.0")
package_b.add_dependency(Factory.create_dependency("A", "*"))
repo.add_package(package_a10)
repo.add_package(package_a11)
repo.add_package(package_a12)
repo.add_package(package_b)
with solver.use_environment(MockEnv((3, 10, 0))):
transaction = solver.solve()
check_solver_result(
transaction,
[
{"job": "install", "package": package_a11}, # only a11, not a12
{"job": "install", "package": package_b},
],
)
def test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_intersection( def test_solver_duplicate_dependencies_ignore_overrides_with_empty_marker_intersection(
solver: Solver, repo: Repository, package: ProjectPackage solver: Solver, repo: Repository, package: ProjectPackage
): ):
......
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