Skip to content

Commit 3728038

Browse files
committed
simplify python_version markers (#851)
Improve recognization and merging of adjacent ranges.
1 parent 629a794 commit 3728038

File tree

4 files changed

+290
-43
lines changed

4 files changed

+290
-43
lines changed

src/poetry/core/version/markers.py

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,9 +723,21 @@ def union_simplify(self, other: BaseMarker) -> BaseMarker | None:
723723
- union between two multimarkers where there are some common markers
724724
and the union of unique markers is a single marker
725725
"""
726+
from poetry.core.packages.utils.utils import get_python_constraint_from_marker
727+
726728
if other in self._markers:
727729
return other
728730

731+
if isinstance(other, SingleMarker) and other.name in PYTHON_VERSION_MARKERS:
732+
# Convert 'python_version >= "3.8" and sys_platform == "linux" or python_version > "3.6"'
733+
# to 'python_version > "3.6"'
734+
for m in self._markers:
735+
if isinstance(m, SingleMarker) and m.name in PYTHON_VERSION_MARKERS:
736+
constraint = get_python_constraint_from_marker(m)
737+
other_constraint = get_python_constraint_from_marker(other)
738+
if other_constraint.allows_all(constraint):
739+
return other
740+
729741
if isinstance(other, MultiMarker):
730742
our_markers = set(self.markers)
731743
their_markers = set(other.markers)
@@ -746,7 +758,13 @@ def union_simplify(self, other: BaseMarker) -> BaseMarker | None:
746758
unique_union = MultiMarker(*unique_markers).union(
747759
MultiMarker(*other_unique_markers)
748760
)
749-
if isinstance(unique_union, (SingleMarkerLike, AnyMarker)):
761+
if isinstance(unique_union, (SingleMarkerLike, AnyMarker)) or (
762+
# Convert 'python_version >= "3.8" and python_version < "3.10"
763+
# or python_version >= "3.10" and python_version < "3.12"'
764+
# to 'python_version >= "3.6" and python_version < "3.12"'
765+
isinstance(unique_union, MultiMarker)
766+
and unique_union.complexity <= (2, 2)
767+
):
750768
common_markers = [
751769
marker for marker in self.markers if marker in shared_markers
752770
]
@@ -856,12 +874,19 @@ def of(cls, *markers: BaseMarker) -> BaseMarker:
856874

857875
# If we have a SingleMarker then with any luck after union it'll
858876
# become another SingleMarker.
877+
# Especially, for `python_version` markers a multi marker is also
878+
# an improvement. E.g. the union of 'python_version == "3.6"' and
879+
# 'python_version == "3.7" or python_version == "3.8"' is
880+
# 'python_version >= "3.6" and python_version < "3.9"'.
859881
if not is_one_multi and isinstance(mark, SingleMarkerLike):
860882
new_marker = mark.union(marker)
861883
if new_marker.is_any():
862884
return AnyMarker()
863885

864-
if isinstance(new_marker, SingleMarkerLike):
886+
if isinstance(new_marker, SingleMarkerLike) or (
887+
isinstance(new_marker, MultiMarker)
888+
and new_marker.complexity <= (2, 2)
889+
):
865890
new_markers[i] = new_marker
866891
included = True
867892
break
@@ -903,9 +928,21 @@ def intersect_simplify(self, other: BaseMarker) -> BaseMarker | None:
903928
- intersection between two markerunions where there are some common markers
904929
and the intersection of unique markers is not a single marker
905930
"""
931+
from poetry.core.packages.utils.utils import get_python_constraint_from_marker
932+
906933
if other in self._markers:
907934
return other
908935

936+
if isinstance(other, SingleMarker) and other.name in PYTHON_VERSION_MARKERS:
937+
# Convert '(python_version >= "3.6" or sys_platform == "linux") and python_version > "3.8"'
938+
# to 'python_version > "3.8"'
939+
for m in self._markers:
940+
if isinstance(m, SingleMarker) and m.name in PYTHON_VERSION_MARKERS:
941+
constraint = get_python_constraint_from_marker(m)
942+
other_constraint = get_python_constraint_from_marker(other)
943+
if constraint.allows_all(other_constraint):
944+
return other
945+
909946
if isinstance(other, MarkerUnion):
910947
our_markers = set(self.markers)
911948
their_markers = set(other.markers)
@@ -926,7 +963,14 @@ def intersect_simplify(self, other: BaseMarker) -> BaseMarker | None:
926963
unique_intersection = MarkerUnion(*unique_markers).intersect(
927964
MarkerUnion(*other_unique_markers)
928965
)
929-
if isinstance(unique_intersection, (SingleMarkerLike, EmptyMarker)):
966+
if isinstance(unique_intersection, (SingleMarkerLike, EmptyMarker)) or (
967+
# Convert '(python_version == "3.6" or python_version >= "3.8)"
968+
# and (python_version >= "3.6" and python_version < "3.8"
969+
# or python_version == "3.9")'
970+
# to 'python_version == "3.6" or python_version == "3.9"'
971+
isinstance(unique_intersection, MarkerUnion)
972+
and unique_intersection.complexity <= (2, 2)
973+
):
930974
common_markers = [
931975
marker for marker in self.markers if marker in shared_markers
932976
]
@@ -1247,16 +1291,38 @@ def _merge_single_markers(
12471291
result_marker = EmptyMarker()
12481292

12491293
elif isinstance(result_constraint, VersionUnion) and merge_class == MarkerUnion:
1250-
# Convert 'python_version == "3.8" or python_version >= "3.9"'
1251-
# to 'python_version >= "3.8"'.
1252-
# Convert 'python_version <= "3.8" or python_version >= "3.9"' to "any".
12531294
result_constraint = get_python_constraint_from_marker(marker1).union(
12541295
get_python_constraint_from_marker(marker2)
12551296
)
12561297
if result_constraint.is_any():
1298+
# Convert 'python_version <= "3.8" or python_version >= "3.9"' to "any".
12571299
result_marker = AnyMarker()
12581300
elif result_constraint.is_simple():
1301+
# Convert 'python_version == "3.8" or python_version >= "3.9"'
1302+
# to 'python_version >= "3.8"'.
12591303
result_marker = SingleMarker(marker1.name, result_constraint)
1304+
elif isinstance(result_constraint, VersionRange):
1305+
# Convert 'python_version' == "3.8" or python_version == "3.9"'
1306+
# to 'python_version >= "3.8" and python_version < "3.10"'.
1307+
# Although both markers have the same complexity, the latter behaves
1308+
# better if it is merged with 'python_version == "3.10' in a next step
1309+
# for example.
1310+
result_marker = MultiMarker(
1311+
SingleMarker(
1312+
marker1.name,
1313+
VersionRange(
1314+
min=result_constraint.min,
1315+
include_min=result_constraint.include_min,
1316+
),
1317+
),
1318+
SingleMarker(
1319+
marker1.name,
1320+
VersionRange(
1321+
max=result_constraint.max,
1322+
include_max=result_constraint.include_max,
1323+
),
1324+
),
1325+
)
12601326

12611327
return result_marker
12621328

tests/packages/test_main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ def test_dependency_from_pep_508_with_python_version() -> None:
7373
assert dep.name == "requests"
7474
assert str(dep.constraint) == "2.18.0"
7575
assert dep.extras == frozenset()
76-
assert dep.python_versions == "~2.7 || ~2.6"
77-
assert str(dep.marker) == 'python_version == "2.7" or python_version == "2.6"'
76+
assert dep.python_versions == ">=2.6 <2.8"
77+
assert str(dep.marker) == 'python_version >= "2.6" and python_version < "2.8"'
7878

7979

8080
def test_dependency_from_pep_508_with_single_python_version() -> None:

tests/packages/utils/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
),
4747
(
4848
'python_version == "2.7" or python_version == "2.6"',
49-
{"python_version": [[("==", "2.7")], [("==", "2.6")]]},
49+
{"python_version": [[(">=", "2.6"), ("<", "2.8")]]},
5050
),
5151
(
5252
(

0 commit comments

Comments
 (0)