diff --git a/src/packaging/markers.py b/src/packaging/markers.py index fb7f49cf8..a996f27d8 100644 --- a/src/packaging/markers.py +++ b/src/packaging/markers.py @@ -15,6 +15,7 @@ from ._tokenizer import ParserSyntaxError from .specifiers import InvalidSpecifier, Specifier from .utils import canonicalize_name +from .version import InvalidVersion, Version __all__ = [ "InvalidMarker", @@ -175,12 +176,20 @@ def _format_marker( def _eval_op(lhs: str, op: Op, rhs: str) -> bool: + # PEP 508 - Environment Markers + # https://peps.python.org/pep-0508/#environment-markers + # > The operators use the PEP 440 version comparison rules + # > when those are defined (that is when both sides have a valid version + # > specifier). If there is no defined PEP 440 behaviour and the operator + # > exists in Python, then the operator falls back to the Python behaviour. + # > Otherwise an error should be raised. try: spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: + lhs_ver = Version(lhs) + except (InvalidSpecifier, InvalidVersion): pass else: - return spec.contains(lhs, prereleases=True) + return spec.contains(lhs_ver, prereleases=True) oper: Operator | None = _operators.get(op.serialize()) if oper is None: diff --git a/tests/test_markers.py b/tests/test_markers.py index faf0d13b9..a741b02cf 100644 --- a/tests/test_markers.py +++ b/tests/test_markers.py @@ -314,6 +314,14 @@ def test_environment_with_extra_none(self): {"extra": "different__punctuation_is_EQUAL"}, True, ), + # extra name that is also a valid version - version comparison applies + # if both sides are valid versions + ("extra == 'v8'", {"extra": "quux"}, False), + ("extra == 'v8'", {"extra": "v8"}, True), + # LHS and RHS are valid versions, so equality uses normalized version + ("extra == 'v8-dev'", {"extra": "v8-dev.0"}, True), + # LHS and RHS are not valid versions, so equality is str comparison + ("extra == 'v8-foo'", {"extra": "v8-foo.0"}, False), ], ) def test_evaluates(self, marker_string, environment, expected):