Skip to content

Commit

Permalink
refactor(provider): extract handling of any marker dependencies into …
Browse files Browse the repository at this point in the history
…separate method
  • Loading branch information
radoering committed Nov 25, 2022
1 parent d12fc1b commit 16fb395
Showing 1 changed file with 68 additions and 63 deletions.
131 changes: 68 additions & 63 deletions src/poetry/puzzle/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,69 +740,7 @@ def fmt_warning(d: Dependency) -> str:
f"<warning>Different requirements found for {warnings}.</warning>"
)

# We need to check if one of the duplicate dependencies
# has no markers. If there is one, we need to change its
# environment markers to the inverse of the union of the
# other dependencies markers.
# For instance, if we have the following dependencies:
# - ipython
# - ipython (1.2.4) ; implementation_name == "pypy"
#
# the marker for `ipython` will become `implementation_name != "pypy"`.
#
# Further, we have to merge the constraints of the requirements
# without markers into the constraints of the requirements with markers.
# for instance, if we have the following dependencies:
# - foo (>= 1.2)
# - foo (!= 1.2.1) ; python == 3.10
#
# the constraint for the second entry will become (!= 1.2.1, >= 1.2)
any_markers_dependencies = [d for d in deps if d.marker.is_any()]
other_markers_dependencies = [d for d in deps 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
for other_dep in other_markers_dependencies[1:]:
marker = marker.union(other_dep.marker)
inverted_marker = marker.invert()

if (
not inverted_marker.is_empty()
and self._python_constraint.allows_any(
get_python_constraint_from_marker(inverted_marker)
)
and (not self._env or inverted_marker.validate(self._env.marker_env))
):
if any_markers_dependencies:
for dep_any in any_markers_dependencies:
dep_any.marker = inverted_marker
else:
# if there is no any marker dependency
# and the inverted marker is not empty,
# a dependency with the inverted union of all markers is required
# in order to not miss other dependencies later, for instance:
# - foo (1.0) ; python == 3.7
# - foo (2.0) ; python == 3.8
# - bar (2.0) ; python == 3.8
# - bar (3.0) ; python == 3.9
#
# the last dependency would be missed without this,
# because the intersection with both foo dependencies is empty
inverted_marker_dep = deps[0].with_constraint(EmptyConstraint())
inverted_marker_dep.marker = inverted_marker
deps.append(inverted_marker_dep)
else:
# Attention: Iterating over any_markers_dependencies and removing them
# from deps may remove wrong dependencies because markers are not
# considered in Dependency.__eq__
for dep in deps[:]:
if dep.marker.is_any():
deps.remove(dep)
self._handle_any_marker_dependencies(deps)

overrides = []
overrides_marker_intersection: BaseMarker = AnyMarker()
Expand Down Expand Up @@ -1035,3 +973,70 @@ def _merge_dependencies_by_marker(
)
deps.append(_deps[0].with_constraint(new_constraint))
return deps

def _handle_any_marker_dependencies(self, dependencies: list[Dependency]) -> None:
"""
We need to check if one of the duplicate dependencies
has no markers. If there is one, we need to change its
environment markers to the inverse of the union of the
other dependencies markers.
For instance, if we have the following dependencies:
- ipython
- ipython (1.2.4) ; implementation_name == "pypy"
the marker for `ipython` will become `implementation_name != "pypy"`.
Further, we have to merge the constraints of the requirements
without markers into the constraints of the requirements with markers.
for instance, if we have the following dependencies:
- foo (>= 1.2)
- foo (!= 1.2.1) ; python == 3.10
the constraint for the second entry will become (!= 1.2.1, >= 1.2).
"""
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()]

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
for other_dep in other_markers_dependencies[1:]:
marker = marker.union(other_dep.marker)
inverted_marker = marker.invert()

if (
not inverted_marker.is_empty()
and self._python_constraint.allows_any(
get_python_constraint_from_marker(inverted_marker)
)
and (not self._env or inverted_marker.validate(self._env.marker_env))
):
if any_markers_dependencies:
for dep_any in any_markers_dependencies:
dep_any.marker = inverted_marker
else:
# if there is no any marker dependency
# and the inverted marker is not empty,
# a dependency with the inverted union of all markers is required
# in order to not miss other dependencies later, for instance:
# - foo (1.0) ; python == 3.7
# - foo (2.0) ; python == 3.8
# - bar (2.0) ; python == 3.8
# - bar (3.0) ; python == 3.9
#
# 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:
# Attention: Iterating over any_markers_dependencies and removing them
# from dependencies may remove wrong dependencies because markers are not
# considered in Dependency.__eq__
for dep in dependencies[:]:
if dep.marker.is_any():
dependencies.remove(dep)

0 comments on commit 16fb395

Please sign in to comment.