Skip to content

Commit faad967

Browse files
committed
simplify python_version markers
1 parent 629a794 commit faad967

File tree

4 files changed

+185
-12
lines changed

4 files changed

+185
-12
lines changed

src/poetry/core/version/markers.py

Lines changed: 54 additions & 4 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)
@@ -856,12 +868,16 @@ def of(cls, *markers: BaseMarker) -> BaseMarker:
856868

857869
# If we have a SingleMarker then with any luck after union it'll
858870
# become another SingleMarker.
871+
# Especially, for `python_version` markers a multi marker is also
872+
# an improvement. E.g. the union of 'python_version == "3.6"' and
873+
# 'python_version == "3.7" or python_version == "3.8"' is
874+
# 'python_version >= "3.6" and python_version < "3.9"'.
859875
if not is_one_multi and isinstance(mark, SingleMarkerLike):
860876
new_marker = mark.union(marker)
861877
if new_marker.is_any():
862878
return AnyMarker()
863879

864-
if isinstance(new_marker, SingleMarkerLike):
880+
if isinstance(new_marker, (SingleMarkerLike, MultiMarker)):
865881
new_markers[i] = new_marker
866882
included = True
867883
break
@@ -903,9 +919,21 @@ def intersect_simplify(self, other: BaseMarker) -> BaseMarker | None:
903919
- intersection between two markerunions where there are some common markers
904920
and the intersection of unique markers is not a single marker
905921
"""
922+
from poetry.core.packages.utils.utils import get_python_constraint_from_marker
923+
906924
if other in self._markers:
907925
return other
908926

927+
if isinstance(other, SingleMarker) and other.name in PYTHON_VERSION_MARKERS:
928+
# Convert '(python_version >= "3.6" or sys_platform == "linux") and python_version > "3.8"'
929+
# to 'python_version > "3.8"'
930+
for m in self._markers:
931+
if isinstance(m, SingleMarker) and m.name in PYTHON_VERSION_MARKERS:
932+
constraint = get_python_constraint_from_marker(m)
933+
other_constraint = get_python_constraint_from_marker(other)
934+
if constraint.allows_all(other_constraint):
935+
return other
936+
909937
if isinstance(other, MarkerUnion):
910938
our_markers = set(self.markers)
911939
their_markers = set(other.markers)
@@ -1247,16 +1275,38 @@ def _merge_single_markers(
12471275
result_marker = EmptyMarker()
12481276

12491277
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".
12531278
result_constraint = get_python_constraint_from_marker(marker1).union(
12541279
get_python_constraint_from_marker(marker2)
12551280
)
12561281
if result_constraint.is_any():
1282+
# Convert 'python_version <= "3.8" or python_version >= "3.9"' to "any".
12571283
result_marker = AnyMarker()
12581284
elif result_constraint.is_simple():
1285+
# Convert 'python_version == "3.8" or python_version >= "3.9"'
1286+
# to 'python_version >= "3.8"'.
12591287
result_marker = SingleMarker(marker1.name, result_constraint)
1288+
elif isinstance(result_constraint, VersionRange):
1289+
# Convert 'python_version' == "3.8" or python_version == "3.9"'
1290+
# to 'python_version >= "3.8" and python_version < "3.10"'.
1291+
# Although both markers have the same complexity, the latter behaves
1292+
# better if it is merged with 'python_version == "3.10' in a next step
1293+
# for example.
1294+
result_marker = MultiMarker(
1295+
SingleMarker(
1296+
marker1.name,
1297+
VersionRange(
1298+
min=result_constraint.min,
1299+
include_min=result_constraint.include_min,
1300+
),
1301+
),
1302+
SingleMarker(
1303+
marker1.name,
1304+
VersionRange(
1305+
max=result_constraint.max,
1306+
include_max=result_constraint.include_max,
1307+
),
1308+
),
1309+
)
12601310

12611311
return result_marker
12621312

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
(

tests/version/test_markers.py

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,96 @@ def test_multi_marker_union_multi_is_multi(
897897
@pytest.mark.parametrize(
898898
"marker1, marker2, expected",
899899
[
900+
# Equivalent ranges
901+
(
902+
'python_version == "3.8" or python_version == "3.9"',
903+
'python_version >= "3.8" and python_version <= "3.9"',
904+
'python_version >= "3.8" and python_version < "3.10"',
905+
),
906+
(
907+
'python_version == "3.8" or python_version == "3.9"',
908+
(
909+
'python_version >= "3.8" and python_version <= "3.9"'
910+
' and sys_platform == "linux"'
911+
),
912+
'python_version >= "3.8" and python_version < "3.10"',
913+
),
914+
(
915+
'python_version == "3.8" or python_version == "3.9"',
916+
(
917+
'python_version >= "3.8" and python_version <= "3.9"'
918+
' and sys_platform == "linux"'
919+
),
920+
'python_version >= "3.8" and python_version < "3.10"',
921+
),
922+
(
923+
'python_version == "3.8" or python_version == "3.9"',
924+
(
925+
'python_version >= "3.8" and python_version < "3.10"'
926+
' and sys_platform == "linux"'
927+
),
928+
'python_version >= "3.8" and python_version < "3.10"',
929+
),
930+
(
931+
'python_version == "3.8" or python_version == "3.9"',
932+
(
933+
'python_version > "3.7" and python_version < "3.10"'
934+
' and sys_platform == "linux"'
935+
),
936+
'python_version > "3.7" and python_version < "3.10"',
937+
),
938+
(
939+
'python_version == "3.8" or python_version == "3.9"',
940+
(
941+
'python_version > "3.7" and python_version <= "3.9"'
942+
' and sys_platform == "linux"'
943+
),
944+
'python_version > "3.7" and python_version < "3.10"',
945+
),
946+
(
947+
(
948+
'python_version == "3.8" or python_version == "3.9"'
949+
' or python_version == "3.10"'
950+
),
951+
(
952+
'python_version >= "3.8" and python_version <= "3.10"'
953+
' and sys_platform == "linux"'
954+
),
955+
'python_version >= "3.8" and python_version <= "3.10"',
956+
),
957+
(
958+
(
959+
'python_version == "3.8" or python_version == "3.9"'
960+
' or python_version == "3.10"'
961+
),
962+
(
963+
'python_version >= "3.8" and python_version < "3.11"'
964+
' and sys_platform == "linux"'
965+
),
966+
'python_version >= "3.8" and python_version < "3.11"',
967+
),
968+
(
969+
(
970+
'python_version == "3.8" or python_version == "3.9"'
971+
' or python_version == "3.10"'
972+
),
973+
(
974+
'python_version > "3.7" and python_version <= "3.10"'
975+
' and sys_platform == "linux"'
976+
),
977+
'python_version > "3.7" and python_version <= "3.10"',
978+
),
979+
(
980+
(
981+
'python_version == "3.8" or python_version == "3.9"'
982+
' or python_version == "3.10"'
983+
),
984+
(
985+
'python_version > "3.7" and python_version < "3.11"'
986+
' and sys_platform == "linux"'
987+
),
988+
'python_version > "3.7" and python_version < "3.11"',
989+
),
900990
# Ranges with same start
901991
(
902992
'python_version >= "3.6" and python_full_version < "3.6.2"',
@@ -913,7 +1003,12 @@ def test_multi_marker_union_multi_is_multi(
9131003
'python_version > "3.6" and python_version < "3.8"',
9141004
'python_version > "3.6" and python_version < "3.8"',
9151005
),
916-
# Ranges meet exactly
1006+
(
1007+
'python_version > "3.7"',
1008+
'python_version >= "3.8" and sys_platform == "linux"',
1009+
'python_version > "3.7"',
1010+
),
1011+
# Ranges meet exactly (adjacent ranges)
9171012
(
9181013
'python_version >= "3.6" and python_full_version < "3.6.2"',
9191014
'python_full_version >= "3.6.2" and python_version < "3.7"',
@@ -934,6 +1029,21 @@ def test_multi_marker_union_multi_is_multi(
9341029
'python_full_version > "3.6.2" and python_version < "3.8"',
9351030
'python_version >= "3.6" and python_version < "3.8"',
9361031
),
1032+
(
1033+
'python_version >= "3.8" and python_version < "3.9"',
1034+
'python_version >= "3.9" and python_version < "3.11"',
1035+
'python_version >= "3.8" and python_version < "3.11"',
1036+
),
1037+
(
1038+
'python_version >= "3.8" and python_version < "3.9"',
1039+
'python_version >= "3.9" and python_version < "3.10"',
1040+
'python_version >= "3.8" and python_version < "3.10"',
1041+
),
1042+
(
1043+
'python_version == "3.8"',
1044+
'python_version == "3.9"',
1045+
'python_version >= "3.8" and python_version < "3.10"',
1046+
),
9371047
# Ranges overlap
9381048
(
9391049
'python_version >= "3.6" and python_full_version <= "3.6.8"',
@@ -992,6 +1102,19 @@ def test_multi_marker_union_multi_is_multi(
9921102
'python_version == "3.7" and implementation_name == "cpython"',
9931103
'python_version >= "3.6" and python_version <= "3.7"',
9941104
),
1105+
# complex
1106+
(
1107+
'python_version == "3.10" and platform_system == "Linux"',
1108+
(
1109+
'python_version == "3.11" and platform_system != "Darwin"'
1110+
' or platform_system != "Linux" and platform_system != "Darwin"'
1111+
' and python_version <= "3.11" and python_full_version >= "3.10.0"'
1112+
),
1113+
(
1114+
'python_version >= "3.10" and python_version <= "3.11"'
1115+
' and platform_system != "Darwin"'
1116+
),
1117+
),
9951118
],
9961119
)
9971120
def test_version_ranges_collapse_on_union(
@@ -2177,8 +2300,8 @@ def test_complex_union() -> None:
21772300
]
21782301
assert (
21792302
str(union(*markers))
2180-
== 'python_version >= "3.6" and platform_system == "Darwin"'
2181-
' and platform_machine == "arm64" or python_version >= "3.10"'
2303+
== 'platform_system == "Darwin" and platform_machine == "arm64"'
2304+
' and python_version >= "3.6" or python_version >= "3.10"'
21822305
)
21832306

21842307

@@ -2210,8 +2333,8 @@ def test_complex_intersection() -> None:
22102333
]
22112334
assert (
22122335
str(dnf(intersection(*markers).invert()))
2213-
== 'python_version >= "3.6" and platform_system == "Darwin"'
2214-
' and platform_machine == "arm64" or python_version >= "3.10"'
2336+
== 'platform_system == "Darwin" and platform_machine == "arm64"'
2337+
' and python_version >= "3.6" or python_version >= "3.10"'
22152338
)
22162339

22172340

0 commit comments

Comments
 (0)