From 45684c270cacbddab706371277def4f8641d092a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 8 May 2022 14:21:56 +0200 Subject: [PATCH 1/7] markers: new function "dnf" to transform a marker into its disjunctive normal form (DNF) This is useful when extracting a python_constraint from a marker. --- src/poetry/core/version/markers.py | 16 +++ tests/version/test_markers.py | 199 +++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) diff --git a/src/poetry/core/version/markers.py b/src/poetry/core/version/markers.py index 649610ef8..911a13752 100644 --- a/src/poetry/core/version/markers.py +++ b/src/poetry/core/version/markers.py @@ -1,5 +1,6 @@ from __future__ import annotations +import itertools import re from typing import TYPE_CHECKING @@ -841,3 +842,18 @@ def _compact_markers(tree_elements: Tree, tree_prefix: str = "") -> BaseMarker: return groups[0] return MarkerUnion.of(*groups) + + +def dnf(marker: BaseMarker) -> BaseMarker: + """Transforms the marker into DNF (disjunctive normal form).""" + if isinstance(marker, MultiMarker): + dnf_markers = [dnf(m) for m in marker.markers] + sub_marker_lists = [ + m.markers if isinstance(m, MarkerUnion) else [m] for m in dnf_markers + ] + return MarkerUnion.of( + *[MultiMarker.of(*c) for c in itertools.product(*sub_marker_lists)] + ) + if isinstance(marker, MarkerUnion): + return MarkerUnion.of(*[dnf(m) for m in marker.markers]) + return marker diff --git a/tests/version/test_markers.py b/tests/version/test_markers.py index 48fda706d..992b6d2fb 100644 --- a/tests/version/test_markers.py +++ b/tests/version/test_markers.py @@ -2,14 +2,23 @@ import os +from typing import TYPE_CHECKING + import pytest +from poetry.core.version.markers import AnyMarker +from poetry.core.version.markers import EmptyMarker from poetry.core.version.markers import MarkerUnion from poetry.core.version.markers import MultiMarker from poetry.core.version.markers import SingleMarker +from poetry.core.version.markers import dnf from poetry.core.version.markers import parse_marker +if TYPE_CHECKING: + from poetry.core.version.markers import BaseMarker + + def test_single_marker() -> None: m = parse_marker('sys_platform == "darwin"') @@ -960,3 +969,193 @@ def test_union_should_drop_markers_if_their_complement_is_present( m = parse_marker(marker) assert parse_marker(expected) == m + + +@pytest.mark.parametrize( + "scheme, marker, expected", + [ + ("empty", EmptyMarker(), EmptyMarker()), + ("any", AnyMarker(), AnyMarker()), + ( + "A_", + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", ">=3.7"), + ), + ( + "AB_", + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", "<3.9"), + ), + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", "<3.9"), + ), + ), + ( + "A+B_", + MarkerUnion( + SingleMarker("python_version", "<3.7"), + SingleMarker("python_version", ">=3.9"), + ), + MarkerUnion( + SingleMarker("python_version", "<3.7"), + SingleMarker("python_version", ">=3.9"), + ), + ), + ( + "AB+AC_", + MarkerUnion( + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", "<3.9"), + ), + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("sys_platform", "linux"), + ), + ), + MarkerUnion( + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", "<3.9"), + ), + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("sys_platform", "linux"), + ), + ), + ), + ( + "A(B+C)_AB+AC", + MultiMarker( + SingleMarker("python_version", ">=3.7"), + MarkerUnion( + SingleMarker("python_version", "<3.9"), + SingleMarker("sys_platform", "linux"), + ), + ), + MarkerUnion( + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", "<3.9"), + ), + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("sys_platform", "linux"), + ), + ), + ), + ( + "(A+B)(C+D)_AC+AD+BC+BD", + MultiMarker( + MarkerUnion( + SingleMarker("python_version", ">=3.7"), + SingleMarker("sys_platform", "win32"), + ), + MarkerUnion( + SingleMarker("python_version", "<3.9"), + SingleMarker("sys_platform", "linux"), + ), + ), + MarkerUnion( + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("python_version", "<3.9"), + ), + MultiMarker( + SingleMarker("python_version", ">=3.7"), + SingleMarker("sys_platform", "linux"), + ), + MultiMarker( + SingleMarker("sys_platform", "win32"), + SingleMarker("python_version", "<3.9"), + ), + ), + ), + ( + "A(B+C)+(D+E)(F+G)_AB+AC+DF+DG+EF+DG", + MarkerUnion( + MultiMarker( + SingleMarker("sys_platform", "win32"), + MarkerUnion( + SingleMarker("python_version", "<3.7"), + SingleMarker("python_version", ">=3.9"), + ), + ), + MultiMarker( + MarkerUnion( + SingleMarker("python_version", "<3.8"), + SingleMarker("python_version", ">=3.9"), + ), + MarkerUnion( + SingleMarker("sys_platform", "linux"), + SingleMarker("python_version", ">=3.9"), + ), + ), + ), + MarkerUnion( + MultiMarker( + SingleMarker("sys_platform", "win32"), + SingleMarker("python_version", "<3.7"), + ), + SingleMarker("python_version", ">=3.9"), + MultiMarker( + SingleMarker("python_version", "<3.8"), + SingleMarker("sys_platform", "linux"), + ), + ), + ), + ( + "(A+B(C+D))(E+F)_AE+AF+BCE+BCF+BDE+BDF", + MultiMarker( + MarkerUnion( + SingleMarker("python_version", ">=3.9"), + MultiMarker( + SingleMarker("implementation_name", "cpython"), + MarkerUnion( + SingleMarker("python_version", "<3.7"), + SingleMarker("python_version", ">=3.8"), + ), + ), + ), + MarkerUnion( + SingleMarker("sys_platform", "win32"), + SingleMarker("sys_platform", "linux"), + ), + ), + MarkerUnion( + MultiMarker( + SingleMarker("python_version", ">=3.9"), + SingleMarker("sys_platform", "win32"), + ), + MultiMarker( + SingleMarker("python_version", ">=3.9"), + SingleMarker("sys_platform", "linux"), + ), + MultiMarker( + SingleMarker("implementation_name", "cpython"), + SingleMarker("python_version", "<3.7"), + SingleMarker("sys_platform", "win32"), + ), + MultiMarker( + SingleMarker("implementation_name", "cpython"), + SingleMarker("python_version", "<3.7"), + SingleMarker("sys_platform", "linux"), + ), + MultiMarker( + SingleMarker("implementation_name", "cpython"), + SingleMarker("python_version", ">=3.8"), + SingleMarker("sys_platform", "win32"), + ), + MultiMarker( + SingleMarker("implementation_name", "cpython"), + SingleMarker("python_version", ">=3.8"), + SingleMarker("sys_platform", "linux"), + ), + ), + ), + ], +) +def test_dnf(scheme: str, marker: BaseMarker, expected: BaseMarker) -> None: + assert dnf(marker) == expected From 905873d9f1d8d79ca38b9e64af4cb8c58b9a0761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 8 May 2022 14:24:34 +0200 Subject: [PATCH 2/7] refactor(tests): parameterized test instead of single test with multiple sub tests --- tests/packages/utils/test_utils.py | 53 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index 97af9df78..022e8ab99 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -8,30 +8,35 @@ from poetry.core.version.markers import parse_marker -def test_convert_markers() -> None: - marker = parse_marker( - 'sys_platform == "win32" and python_version < "3.6" or sys_platform == "linux"' - ' and python_version < "3.6" and python_version >= "3.3" or sys_platform ==' - ' "darwin" and python_version < "3.3"' - ) - converted = convert_markers(marker) - assert converted["python_version"] == [ - [("<", "3.6")], - [("<", "3.6"), (">=", "3.3")], - [("<", "3.3")], - ] - - marker = parse_marker( - 'sys_platform == "win32" and python_version < "3.6" or sys_platform == "win32"' - ' and python_version < "3.6" and python_version >= "3.3" or sys_platform ==' - ' "win32" and python_version < "3.3"' - ) - converted = convert_markers(marker) - assert converted["python_version"] == [[("<", "3.6")]] - - marker = parse_marker('python_version == "2.7" or python_version == "2.6"') - converted = convert_markers(marker) - assert converted["python_version"] == [[("==", "2.7")], [("==", "2.6")]] +@pytest.mark.parametrize( + "marker, expected", + [ + ( + 'sys_platform == "win32" and python_version < "3.6" or sys_platform ==' + ' "linux" and python_version < "3.6" and python_version >= "3.3" or' + ' sys_platform == "darwin" and python_version < "3.3"', + [ + [("<", "3.6")], + [("<", "3.6"), (">=", "3.3")], + [("<", "3.3")], + ], + ), + ( + 'sys_platform == "win32" and python_version < "3.6" or sys_platform ==' + ' "win32" and python_version < "3.6" and python_version >= "3.3" or' + ' sys_platform == "win32" and python_version < "3.3"', + [[("<", "3.6")]], + ), + ( + 'python_version == "2.7" or python_version == "2.6"', + [[("==", "2.7")], [("==", "2.6")]], + ), + ], +) +def test_convert_markers(marker: str, expected: list[list[tuple[str, str]]]) -> None: + parsed_marker = parse_marker(marker) + converted = convert_markers(parsed_marker) + assert converted["python_version"] == expected @pytest.mark.parametrize( From ce7e15f0f224688312bc40668e1d1d2b0922a3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 8 May 2022 16:58:51 +0200 Subject: [PATCH 3/7] tests: increase test coverage by checking entire result instead of part of it --- tests/packages/utils/test_utils.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index 022e8ab99..8669337a4 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -15,28 +15,37 @@ 'sys_platform == "win32" and python_version < "3.6" or sys_platform ==' ' "linux" and python_version < "3.6" and python_version >= "3.3" or' ' sys_platform == "darwin" and python_version < "3.3"', - [ - [("<", "3.6")], - [("<", "3.6"), (">=", "3.3")], - [("<", "3.3")], - ], + { + "python_version": [ + [("<", "3.6")], + [("<", "3.6"), (">=", "3.3")], + [("<", "3.3")], + ], + "sys_platform": [ + [("==", "win32")], + [("==", "linux")], + [("==", "darwin")], + ], + }, ), ( 'sys_platform == "win32" and python_version < "3.6" or sys_platform ==' ' "win32" and python_version < "3.6" and python_version >= "3.3" or' ' sys_platform == "win32" and python_version < "3.3"', - [[("<", "3.6")]], + {"python_version": [[("<", "3.6")]], "sys_platform": [[("==", "win32")]]}, ), ( 'python_version == "2.7" or python_version == "2.6"', - [[("==", "2.7")], [("==", "2.6")]], + {"python_version": [[("==", "2.7")], [("==", "2.6")]]}, ), ], ) -def test_convert_markers(marker: str, expected: list[list[tuple[str, str]]]) -> None: +def test_convert_markers( + marker: str, expected: dict[str, list[list[tuple[str, str]]]] +) -> None: parsed_marker = parse_marker(marker) converted = convert_markers(parsed_marker) - assert converted["python_version"] == expected + assert converted == expected @pytest.mark.parametrize( From 6c9f661471910d6ced9f37474139ade8b3563b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 8 May 2022 14:28:37 +0200 Subject: [PATCH 4/7] packages: fix get_python_constraint_from_marker via convert_markers for markers which are not in disjunctive normal form (DNF) --- src/poetry/core/packages/utils/utils.py | 11 ++++++++++- tests/packages/utils/test_utils.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/poetry/core/packages/utils/utils.py b/src/poetry/core/packages/utils/utils.py index 5ebae1fab..f4b77a563 100644 --- a/src/poetry/core/packages/utils/utils.py +++ b/src/poetry/core/packages/utils/utils.py @@ -11,6 +11,8 @@ from urllib.parse import urlsplit from urllib.request import url2pathname +from poetry.core.version.markers import dnf + if TYPE_CHECKING: from poetry.core.packages.constraints import BaseConstraint @@ -162,7 +164,7 @@ def group_markers( def convert_markers(marker: BaseMarker) -> dict[str, list[list[tuple[str, str]]]]: - groups = group_markers([marker]) + groups = group_markers([dnf(marker)]) requirements = {} @@ -198,6 +200,13 @@ def _group( _group(groups, or_=True) + for group_name in requirements: + # remove duplicates + seen = [] + requirements[group_name] = [ + r for r in requirements[group_name] if not (r in seen or seen.append(r)) + ] + return requirements diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index 8669337a4..54a2e8f13 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -38,6 +38,19 @@ 'python_version == "2.7" or python_version == "2.6"', {"python_version": [[("==", "2.7")], [("==", "2.6")]]}, ), + ( + '(python_version < "2.7" or python_full_version >= "3.0.0") and' + ' python_full_version < "3.6.0"', + {"python_version": [[("<", "2.7")], [(">=", "3.0.0"), ("<", "3.6.0")]]}, + ), + ( + '(python_version < "2.7" or python_full_version >= "3.0.0") and' + ' extra == "foo"', + { + "extra": [[("==", "foo")]], + "python_version": [[("<", "2.7")], [(">=", "3.0.0")]], + }, + ), ], ) def test_convert_markers( @@ -56,6 +69,11 @@ def test_convert_markers( 'python_full_version >= "3.6.1" and python_full_version < "4.0.0"', ">=3.6.1, <4.0.0", ), + ( + '(python_version < "2.7" or python_full_version >= "3.0.0") and' + ' python_full_version < "3.6.0"', + "<2.7 || >=3.0,<3.6", + ), ('sys_platform == "linux"', "*"), ], ) From 17169c63dda2fcb3e67c40b8f6b6d6340c1bbcaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 8 May 2022 15:03:59 +0200 Subject: [PATCH 5/7] packages: increase test coverage for get_python_constraint_from_marker and fix for versions with ".*" --- src/poetry/core/packages/utils/utils.py | 8 +++--- tests/packages/utils/test_utils.py | 35 ++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/poetry/core/packages/utils/utils.py b/src/poetry/core/packages/utils/utils.py index f4b77a563..a53909493 100644 --- a/src/poetry/core/packages/utils/utils.py +++ b/src/poetry/core/packages/utils/utils.py @@ -322,10 +322,12 @@ def get_python_constraint_from_marker( for op, version in or_: # Expand python version if op == "==": - version = "~" + version - op = "" + if "*" not in version: + version = "~" + version + op = "" elif op == "!=": - version += ".*" + if "*" not in version: + version += ".*" elif op in ("<=", ">"): parsed_version = Version.parse(version) if parsed_version.precision == 1: diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index 54a2e8f13..ea61b0c61 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -64,20 +64,53 @@ def test_convert_markers( @pytest.mark.parametrize( ["marker", "constraint"], [ + # == + ('python_version == "3.6"', "~3.6"), + ('python_version == "3.6.*"', "==3.6.*"), + ('python_version == "3.6.* "', "==3.6.*"), + # != + ('python_version != "3.6"', "!=3.6.*"), + ('python_version != "3.6.*"', "!=3.6.*"), + ('python_version != "3.6.* "', "!=3.6.*"), + # <, <=, >, >= precision 1 + ('python_version < "3"', "<3"), + ('python_version <= "3"', "<4"), + ('python_version > "3"', ">=4"), + ('python_version >= "3"', ">=3"), + # <, <=, >, >= precision 2 + ('python_version < "3.6"', "<3.6"), + ('python_version <= "3.6"', "<3.7"), + ('python_version > "3.6"', ">=3.7"), + ('python_version >= "3.6"', ">=3.6"), + # in, not in + ('python_version in "2.7, 3.6"', ">=2.7.0,<2.8.0 || >=3.6.0,<3.7.0"), + ('python_version in "2.7, 3.6.2"', ">=2.7.0,<2.8.0 || 3.6.2"), + ('python_version not in "2.7, 3.6"', "<2.7.0 || >=2.8.0,<3.6.0 || >=3.7.0"), + ('python_version not in "2.7, 3.6.2"', "<2.7.0 || >=2.8.0,<3.6.2 || >3.6.2"), + # and ('python_version >= "3.6" and python_full_version < "4.0"', ">=3.6, <4.0"), ( 'python_full_version >= "3.6.1" and python_full_version < "4.0.0"', ">=3.6.1, <4.0.0", ), + # or + ('python_version < "3.6" or python_version >= "3.9"', "<3.6 || >=3.9"), + # and or + ( + 'python_version >= "3.7" and python_version < "3.8" or python_version >=' + ' "3.9" and python_version < "3.10"', + ">=3.7,<3.8 || >=3.9,<3.10", + ), ( '(python_version < "2.7" or python_full_version >= "3.0.0") and' ' python_full_version < "3.6.0"', "<2.7 || >=3.0,<3.6", ), + # no python_version ('sys_platform == "linux"', "*"), ], ) def test_get_python_constraint_from_marker(marker: str, constraint: str) -> None: marker_parsed = parse_marker(marker) constraint_parsed = parse_constraint(constraint) - assert constraint_parsed == get_python_constraint_from_marker(marker_parsed) + assert get_python_constraint_from_marker(marker_parsed) == constraint_parsed From d0de50d5b1b757315709ff4cb88864168f8898f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Mon, 9 May 2022 17:59:35 +0200 Subject: [PATCH 6/7] packages: fix get_python_constraint_from_marker via convert_markers for markers whose disjunctive normal form (DNF) contains a group without marker name "python_version" By the way, convert_markers() was simplified significantly. That was possible because markers are converted to DNF before conversion. --- src/poetry/core/packages/dependency.py | 3 +- src/poetry/core/packages/utils/utils.py | 86 +++++++++---------------- tests/packages/test_dependency.py | 17 +++-- tests/packages/utils/test_utils.py | 18 ++++++ 4 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/poetry/core/packages/dependency.py b/src/poetry/core/packages/dependency.py index d69735355..7033c42aa 100644 --- a/src/poetry/core/packages/dependency.py +++ b/src/poetry/core/packages/dependency.py @@ -14,6 +14,7 @@ ) from poetry.core.packages.dependency_group import MAIN_GROUP from poetry.core.packages.specification import PackageSpecification +from poetry.core.packages.utils.utils import contains_group_without_marker from poetry.core.semver.helpers import parse_constraint from poetry.core.semver.version_range_constraint import VersionRangeConstraint from poetry.core.version.markers import parse_marker @@ -180,7 +181,7 @@ def marker(self, marker: str | BaseMarker) -> None: # Recalculate python versions. self._python_versions = "*" - if "python_version" in markers: + if not contains_group_without_marker(markers, "python_version"): ors = [] for or_ in markers["python_version"]: ands = [] diff --git a/src/poetry/core/packages/utils/utils.py b/src/poetry/core/packages/utils/utils.py index a53909493..1ae92b88f 100644 --- a/src/poetry/core/packages/utils/utils.py +++ b/src/poetry/core/packages/utils/utils.py @@ -21,6 +21,8 @@ from poetry.core.semver.version_union import VersionUnion from poetry.core.version.markers import BaseMarker + ConvertedMarkers = dict[str, list[list[tuple[str, str]]]] + BZ2_EXTENSIONS = (".tar.bz2", ".tbz") XZ_EXTENSIONS = (".tar.xz", ".txz", ".tlz", ".tar.lz", ".tar.lzma") @@ -138,67 +140,34 @@ def splitext(path: str) -> tuple[str, str]: return base, ext -def group_markers( - markers: list[BaseMarker], or_: bool = False -) -> list[tuple[str, str, str] | list[tuple[str, str, str]]]: +def convert_markers(marker: BaseMarker) -> ConvertedMarkers: from poetry.core.version.markers import MarkerUnion from poetry.core.version.markers import MultiMarker from poetry.core.version.markers import SingleMarker - groups = [[]] - - for marker in markers: - if or_: - groups.append([]) - - if isinstance(marker, (MultiMarker, MarkerUnion)): - groups[-1].append( - group_markers(marker.markers, isinstance(marker, MarkerUnion)) - ) - elif isinstance(marker, SingleMarker): - lhs, op, rhs = marker.name, marker.operator, marker.value - - groups[-1].append((lhs, op, rhs)) - - return groups - - -def convert_markers(marker: BaseMarker) -> dict[str, list[list[tuple[str, str]]]]: - groups = group_markers([dnf(marker)]) - requirements = {} + marker = dnf(marker) + conjunctions = marker.markers if isinstance(marker, MarkerUnion) else [marker] + group_count = len(conjunctions) - def _group( - _groups: list[tuple[str, str, str] | list[tuple[str, str, str]]], - or_: bool = False, + def add_constraint( + marker_name: str, constraint: tuple[str, str], group_index: int ) -> None: - ors = {} - for group in _groups: - if isinstance(group, list): - _group(group, or_=True) - else: - variable, op, value = group - group_name = str(variable) - - # python_full_version is equivalent to python_version - # for Poetry so we merge them - if group_name == "python_full_version": - group_name = "python_version" - - if group_name not in requirements: - requirements[group_name] = [] - - if group_name not in ors: - ors[group_name] = or_ - - if ors[group_name] or not requirements[group_name]: - requirements[group_name].append([]) - - requirements[group_name][-1].append((str(op), str(value))) - - ors[group_name] = False - - _group(groups, or_=True) + # python_full_version is equivalent to python_version + # for Poetry so we merge them + if marker_name == "python_full_version": + marker_name = "python_version" + if marker_name not in requirements: + requirements[marker_name] = [[] for _ in range(group_count)] + requirements[marker_name][group_index].append(constraint) + + for i, sub_marker in enumerate(conjunctions): + if isinstance(sub_marker, MultiMarker): + for m in sub_marker.markers: + if isinstance(m, SingleMarker): + add_constraint(m.name, (m.operator, m.value), i) + elif isinstance(sub_marker, SingleMarker): + add_constraint(sub_marker.name, (sub_marker.operator, sub_marker.value), i) for group_name in requirements: # remove duplicates @@ -210,6 +179,10 @@ def _group( return requirements +def contains_group_without_marker(markers: ConvertedMarkers, marker_name: str) -> bool: + return marker_name not in markers or [] in markers[marker_name] + + def create_nested_marker( name: str, constraint: BaseConstraint | VersionUnion | Version | VersionConstraint, @@ -315,6 +288,11 @@ def get_python_constraint_from_marker( return EmptyConstraint() markers = convert_markers(marker) + if contains_group_without_marker(markers, "python_version"): + # groups are in disjunctive normal form (DNF), + # an empty group means that python_version does not appear in this group, + # which means that python_version is arbitrary for this group + return VersionRange() ors = [] for or_ in markers["python_version"]: diff --git a/tests/packages/test_dependency.py b/tests/packages/test_dependency.py index 6d3e7544f..63a8900f6 100644 --- a/tests/packages/test_dependency.py +++ b/tests/packages/test_dependency.py @@ -324,12 +324,19 @@ def test_with_constraint() -> None: assert new.transitive_python_constraint == dependency.transitive_python_constraint -def test_marker_properly_sets_python_constraint() -> None: +@pytest.mark.parametrize( + "marker, expected", + [ + ('python_version >= "3.6" and python_version < "4.0"', ">=3.6,<4.0"), + ('sys_platform == "linux"', "*"), + ('python_version >= "3.9" or sys_platform == "linux"', "*"), + ('python_version >= "3.9" and sys_platform == "linux"', ">=3.9"), + ], +) +def test_marker_properly_sets_python_constraint(marker: str, expected: str) -> None: dependency = Dependency("foo", "^1.2.3") - - dependency.marker = 'python_version >= "3.6" and python_version < "4.0"' # type: ignore[assignment] - - assert str(dependency.python_constraint) == ">=3.6,<4.0" + dependency.marker = marker # type: ignore[assignment] + assert str(dependency.python_constraint) == expected def test_dependency_markers_are_the_same_as_markers() -> None: diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index ea61b0c61..d8bfcbf0c 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -51,6 +51,20 @@ "python_version": [[("<", "2.7")], [(">=", "3.0.0")]], }, ), + ( + 'python_version >= "3.9" or sys_platform == "linux"', + { + "python_version": [[(">=", "3.9")], []], + "sys_platform": [[], [("==", "linux")]], + }, + ), + ( + 'python_version >= "3.9" and sys_platform == "linux"', + { + "python_version": [[(">=", "3.9")]], + "sys_platform": [[("==", "linux")]], + }, + ), ], ) def test_convert_markers( @@ -108,6 +122,10 @@ def test_convert_markers( ), # no python_version ('sys_platform == "linux"', "*"), + # no relevant python_version + ('python_version >= "3.9" or sys_platform == "linux"', "*"), + # relevant python_version + ('python_version >= "3.9" and sys_platform == "linux"', ">=3.9"), ], ) def test_get_python_constraint_from_marker(marker: str, constraint: str) -> None: From 1183112c1f814388a849b19fe82fea80194c431e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Mon, 9 May 2022 18:47:18 +0200 Subject: [PATCH 7/7] bump version: 1.1.0-alpha.8 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5b6101f27..d679064c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-core" -version = "1.1.0-alpha.7" +version = "1.1.0-alpha.8" description = "Poetry PEP 517 Build Backend" authors = ["Sébastien Eustace "]