Skip to content

Commit b365852

Browse files
committed
perf: filter duplicates (and equivalents) from itertools.product when building the cnf/dnf (python-poetry#851)
1 parent 43005b1 commit b365852

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

src/poetry/core/version/markers.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
if TYPE_CHECKING:
3434
from collections.abc import Callable
3535
from collections.abc import Iterable
36+
from collections.abc import Iterator
3637
from collections.abc import Mapping
38+
from collections.abc import Sequence
3739

3840
from lark import Tree
3941

@@ -1125,7 +1127,7 @@ def cnf(marker: BaseMarker) -> BaseMarker:
11251127
m.markers if isinstance(m, MultiMarker) else [m] for m in cnf_markers
11261128
]
11271129
return MultiMarker.of(
1128-
*[MarkerUnion.of(*c) for c in itertools.product(*sub_marker_lists)]
1130+
*[MarkerUnion.of(*c) for c in _unique_product(*sub_marker_lists)]
11291131
)
11301132

11311133
if isinstance(marker, MultiMarker):
@@ -1143,7 +1145,7 @@ def dnf(marker: BaseMarker) -> BaseMarker:
11431145
m.markers if isinstance(m, MarkerUnion) else [m] for m in dnf_markers
11441146
]
11451147
return MarkerUnion.of(
1146-
*[MultiMarker.of(*c) for c in itertools.product(*sub_marker_lists)]
1148+
*[MultiMarker.of(*c) for c in _unique_product(*sub_marker_lists)]
11471149
)
11481150

11491151
if isinstance(marker, MarkerUnion):
@@ -1225,6 +1227,21 @@ def union(*markers: BaseMarker) -> BaseMarker:
12251227
return min(*candidates, key=lambda x: x.complexity)
12261228

12271229

1230+
def _unique_product(
1231+
*sub_marker_lists: Sequence[BaseMarker],
1232+
) -> Iterator[Sequence[BaseMarker]]:
1233+
"""
1234+
Returns an itertools.product of the sub_marker_lists
1235+
without duplicates (and equivalents) removed while maintaining order.
1236+
"""
1237+
unique_sets = set()
1238+
for sub_marker_list in itertools.product(*sub_marker_lists):
1239+
sub_marker_set = frozenset(sub_marker_list)
1240+
if sub_marker_set not in unique_sets:
1241+
unique_sets.add(sub_marker_set)
1242+
yield sub_marker_list
1243+
1244+
12281245
@functools.cache
12291246
def _merge_single_markers(
12301247
marker1: SingleMarkerLike[SingleMarkerConstraint],

tests/version/test_markers.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,6 +2368,44 @@ def test_complex_intersection() -> None:
23682368
)
23692369

23702370

2371+
def test_complex_intersection_with_itertools_product_duplicates() -> None:
2372+
"""
2373+
Real-world example from https://github.com/python-poetry/poetry/issues/10250.
2374+
(Only occurs if the solver takes an unfortunate path.)
2375+
Takes a long time without filtering duplicates from the itertools.product()
2376+
in cnf/dnf early.
2377+
"""
2378+
m1 = parse_marker(
2379+
'(python_version > "3.9" or platform_system != "Windows"'
2380+
' or platform_machine != "x86") and (python_version != "3.10"'
2381+
' or platform_system != "Windows" or platform_python_implementation == "PyPy")'
2382+
)
2383+
m2 = parse_marker(
2384+
'(platform_system != "Windows" or platform_machine != "x86"'
2385+
' or python_version >= "3.10" and python_version < "3.12")'
2386+
' and (sys_platform != "darwin" or platform_machine != "arm64")'
2387+
' and python_version <= "3.11" and (platform_system != "Windows"'
2388+
' or platform_python_implementation == "PyPy" or python_version == "3.9"'
2389+
' or python_version == "3.11") and python_version >= "3.9"'
2390+
' and (platform_system != "Windows" or platform_python_implementation == "PyPy"'
2391+
' or platform_machine != "x86" or python_version == "3.11")'
2392+
)
2393+
2394+
assert str(m1.intersect(m2)) == (
2395+
'(python_version > "3.9" or platform_system != "Windows"'
2396+
' or platform_machine != "x86") and (python_version != "3.10"'
2397+
' or platform_system != "Windows" or platform_python_implementation == "PyPy")'
2398+
' and (platform_system != "Windows" or platform_machine != "x86"'
2399+
' or python_version >= "3.10") and python_version <= "3.11"'
2400+
' and (sys_platform != "darwin" or platform_machine != "arm64") and'
2401+
' (platform_system != "Windows" or platform_python_implementation == "PyPy"'
2402+
' or python_version == "3.9" or python_version == "3.11")'
2403+
' and python_version >= "3.9" and (platform_system != "Windows"'
2404+
' or platform_python_implementation == "PyPy" or platform_machine != "x86"'
2405+
' or python_version == "3.11")'
2406+
)
2407+
2408+
23712409
def test_complex_union_is_deterministic() -> None:
23722410
"""
23732411
This test might fail sporadically if marker operations are not deterministic!

0 commit comments

Comments
 (0)