Skip to content

Commit 8652187

Browse files
add support for non Python versions in platform_release markers (#722)
e.g. 'platform_release == "4.9.253-tegra"' Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com>
1 parent 80f8a97 commit 8652187

5 files changed

Lines changed: 127 additions & 11 deletions

File tree

src/poetry/core/constraints/version/parser.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ def parse_constraint(constraints: str) -> VersionConstraint:
1919
return _parse_constraint(constraints=constraints)
2020

2121

22-
def parse_marker_version_constraint(constraints: str) -> VersionConstraint:
23-
return _parse_constraint(constraints=constraints, is_marker_constraint=True)
22+
def parse_marker_version_constraint(
23+
constraints: str, *, pep440: bool = True
24+
) -> VersionConstraint:
25+
return _parse_constraint(
26+
constraints=constraints, is_marker_constraint=True, pep440=pep440
27+
)
2428

2529

2630
def _parse_constraint(
27-
constraints: str, *, is_marker_constraint: bool = False
31+
constraints: str, *, is_marker_constraint: bool = False, pep440: bool = True
2832
) -> VersionConstraint:
2933
if constraints == "*":
3034
from poetry.core.constraints.version.version_range import VersionRange
@@ -46,13 +50,17 @@ def _parse_constraint(
4650
for constraint in and_constraints:
4751
constraint_objects.append(
4852
parse_single_constraint(
49-
constraint, is_marker_constraint=is_marker_constraint
53+
constraint,
54+
is_marker_constraint=is_marker_constraint,
55+
pep440=pep440,
5056
)
5157
)
5258
else:
5359
constraint_objects.append(
5460
parse_single_constraint(
55-
and_constraints[0], is_marker_constraint=is_marker_constraint
61+
and_constraints[0],
62+
is_marker_constraint=is_marker_constraint,
63+
pep440=pep440,
5664
)
5765
)
5866

@@ -74,9 +82,10 @@ def _parse_constraint(
7482

7583

7684
def parse_single_constraint(
77-
constraint: str, *, is_marker_constraint: bool = False
85+
constraint: str, *, is_marker_constraint: bool = False, pep440: bool = True
7886
) -> VersionConstraint:
7987
from poetry.core.constraints.version.patterns import BASIC_CONSTRAINT
88+
from poetry.core.constraints.version.patterns import BASIC_RELEASE_CONSTRAINT
8089
from poetry.core.constraints.version.patterns import CARET_CONSTRAINT
8190
from poetry.core.constraints.version.patterns import TILDE_CONSTRAINT
8291
from poetry.core.constraints.version.patterns import TILDE_PEP440_CONSTRAINT
@@ -185,6 +194,35 @@ def parse_single_constraint(
185194

186195
return version
187196

197+
# These below should be reserved for comparing non python packages such as OS
198+
# versions using `platform_release`
199+
if not pep440 and (m := BASIC_RELEASE_CONSTRAINT.match(constraint)):
200+
op = m.group("op")
201+
release_string = m.group("release")
202+
build = m.group("build")
203+
204+
try:
205+
version = Version(
206+
release=Version.parse(release_string).release,
207+
local=build,
208+
)
209+
except InvalidVersion as e:
210+
raise ParseConstraintError(
211+
f"Could not parse version constraint: {constraint}"
212+
) from e
213+
214+
if op == "<":
215+
return VersionRange(max=version)
216+
if op == "<=":
217+
return VersionRange(max=version, include_max=True)
218+
if op == ">":
219+
return VersionRange(min=version)
220+
if op == ">=":
221+
return VersionRange(min=version, include_min=True)
222+
if op == "!=":
223+
return VersionUnion(VersionRange(max=version), VersionRange(min=version))
224+
return version
225+
188226
raise ParseConstraintError(f"Could not parse version constraint: {constraint}")
189227

190228

src/poetry/core/constraints/version/patterns.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,17 @@
2626
rf"^(?P<op><>|!=|>=?|<=?|==?)?\s*(?P<version>{VERSION_PATTERN}|dev)(?P<wildcard>\.\*)?$",
2727
re.VERBOSE | re.IGNORECASE,
2828
)
29+
30+
RELEASE_PATTERN = r"""
31+
(?P<release>[0-9]+(?:\.[0-9]+)*)
32+
(?:(\+|-)(?P<build>
33+
[0-9a-zA-Z-]+
34+
(?:\.[0-9a-zA-Z-]+)*
35+
))?
36+
"""
37+
38+
# pattern for non Python versions such as OS versions in `platform_release`
39+
BASIC_RELEASE_CONSTRAINT = re.compile(
40+
rf"^(?P<op><>|!=|>=?|<=?|==?)?\s*(?P<version>{RELEASE_PATTERN})$",
41+
re.VERBOSE | re.IGNORECASE,
42+
)

src/poetry/core/version/markers.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,15 @@ def __init__(self, name: str, constraint: SingleMarkerConstraint) -> None:
233233
from poetry.core.constraints.generic import (
234234
parse_constraint as parse_generic_constraint,
235235
)
236-
from poetry.core.constraints.version import (
237-
parse_constraint as parse_version_constraint,
238-
)
236+
from poetry.core.constraints.version import parse_marker_version_constraint
239237

240238
self._name = ALIASES.get(name, name)
241239
self._constraint = constraint
242240
self._parser: Callable[[str], BaseConstraint | VersionConstraint]
243241
if isinstance(constraint, VersionConstraint):
244-
self._parser = parse_version_constraint
242+
self._parser = functools.partial(
243+
parse_marker_version_constraint, pep440=name != "platform_release"
244+
)
245245
else:
246246
self._parser = parse_generic_constraint
247247

@@ -386,7 +386,9 @@ def __init__(
386386
# or `"arm" not in platform_version`.
387387
pass
388388
elif name in self._VERSION_LIKE_MARKER_NAME:
389-
parser = parse_marker_version_constraint
389+
parser = functools.partial(
390+
parse_marker_version_constraint, pep440=name != "platform_release"
391+
)
390392

391393
if self._operator in {"in", "not in"}:
392394
versions = []

tests/constraints/version/test_parse_constraint.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from poetry.core.constraints.version import VersionRange
88
from poetry.core.constraints.version import VersionUnion
99
from poetry.core.constraints.version import parse_constraint
10+
from poetry.core.constraints.version import parse_marker_version_constraint
11+
from poetry.core.constraints.version.exceptions import ParseConstraintError
1012
from poetry.core.version.pep440 import ReleaseTag
1113

1214

@@ -595,3 +597,14 @@ def test_parse_constraint_with_white_space_padding(
595597
padding = " " * (4 if with_whitespace_padding else 0)
596598
constraint = padding.join(["", *constraint_parts, ""])
597599
assert parse_constraint(constraint) == expected
600+
601+
602+
def test_parse_marker_constraint_does_not_allow_invalid_version() -> None:
603+
with pytest.raises(ParseConstraintError):
604+
parse_marker_version_constraint("4.9.253-tegra")
605+
606+
607+
def test_parse_marker_constraint_does_allow_invalid_version_if_requested() -> None:
608+
assert parse_marker_version_constraint(
609+
"4.9.253-tegra", pep440=False
610+
) == Version.from_parts(4, 9, 253, local="tegra")

tests/version/test_markers.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from poetry.core.version.markers import AnyMarker
1313
from poetry.core.version.markers import AtomicMarkerUnion
1414
from poetry.core.version.markers import EmptyMarker
15+
from poetry.core.version.markers import InvalidMarker
1516
from poetry.core.version.markers import MarkerUnion
1617
from poetry.core.version.markers import MultiMarker
1718
from poetry.core.version.markers import SingleMarker
@@ -73,6 +74,21 @@ def test_parse_marker(marker: str) -> None:
7374
assert str(parse_marker(marker)) == marker
7475

7576

77+
@pytest.mark.parametrize(
78+
("marker", "valid"),
79+
[
80+
('platform_release != "4.9.253-tegra"', True),
81+
('python_version != "4.9.253-tegra"', False),
82+
],
83+
)
84+
def test_parse_marker_non_python_versions(marker: str, valid: bool) -> None:
85+
if valid:
86+
assert str(parse_marker(marker)) == marker
87+
else:
88+
with pytest.raises(InvalidMarker):
89+
parse_marker(marker)
90+
91+
7692
@pytest.mark.parametrize(
7793
("marker", "expected_name", "expected_constraint"),
7894
[
@@ -963,6 +979,39 @@ def test_multi_marker_removes_duplicates() -> None:
963979
{"platform_machine": "x86_64"},
964980
False,
965981
),
982+
('"tegra" in platform_release', {"platform_release": "5.10.120-tegra"}, True),
983+
('"tegra" in platform_release', {"platform_release": "5.10.120"}, False),
984+
(
985+
'"tegra" not in platform_release',
986+
{"platform_release": "5.10.120-tegra"},
987+
False,
988+
),
989+
('"tegra" not in platform_release', {"platform_release": "5.10.120"}, True),
990+
(
991+
"platform_machine == 'aarch64' and 'tegra' in platform_release",
992+
{"platform_release": "5.10.120-tegra", "platform_machine": "aarch64"},
993+
True,
994+
),
995+
(
996+
"platform_release != '4.9.253-tegra'",
997+
{"platform_release": "4.9.254-tegra"},
998+
True,
999+
),
1000+
(
1001+
"platform_release != '4.9.253-tegra'",
1002+
{"platform_release": "4.9.253"},
1003+
True,
1004+
),
1005+
(
1006+
"platform_release >= '6.6.0+rpt-rpi-v8'",
1007+
{"platform_release": "6.6.20+rpt-rpi-v8"},
1008+
True,
1009+
),
1010+
(
1011+
"platform_release < '5.10.123-tegra' and platform_release >= '4.9.254-tegra'",
1012+
{"platform_release": "4.9.254-tegra"},
1013+
True,
1014+
),
9661015
# extras
9671016
# single extra
9681017
("extra == 'security'", {"extra": "quux"}, False),

0 commit comments

Comments
 (0)