diff --git a/src/packaging/markers.py b/src/packaging/markers.py index 2d60bdf2..7ac7bb69 100644 --- a/src/packaging/markers.py +++ b/src/packaging/markers.py @@ -309,6 +309,12 @@ def evaluate(self, environment: dict[str, str] | None = None) -> bool: """ current_environment = cast("dict[str, str]", default_environment()) current_environment["extra"] = "" + # Work around platform.python_version() returning something that is not PEP 440 + # compliant for non-tagged Python builds. We preserve default_environment()'s + # behavior of returning platform.python_version() verbatim, and leave it to the + # caller to provide a syntactically valid version if they want to override it. + if current_environment["python_full_version"].endswith("+"): + current_environment["python_full_version"] += "local" if environment is not None: current_environment.update(environment) # The API used to allow setting extra to None. We need to handle this diff --git a/tests/test_markers.py b/tests/test_markers.py index a2f92b30..775b51fd 100644 --- a/tests/test_markers.py +++ b/tests/test_markers.py @@ -8,6 +8,7 @@ import platform import sys from typing import cast +from unittest import mock import pytest @@ -19,6 +20,7 @@ default_environment, format_full_version, ) +from packaging.version import InvalidVersion VARIABLES = [ "extra", @@ -384,3 +386,14 @@ def test_extra_str_normalization(self): assert str(Marker(lhs)) == f'"{normalized_name}" == extra' assert str(Marker(rhs)) == f'extra == "{normalized_name}"' + + def test_python_full_version_untagged_user_provided(self): + """A user-provided python_full_version ending with a + fails to parse.""" + with pytest.raises(InvalidVersion): + Marker("python_full_version < '3.12'").evaluate( + {"python_full_version": "3.11.1+"} + ) + + def test_python_full_version_untagged(self): + with mock.patch("platform.python_version", return_value="3.11.1+"): + assert Marker("python_full_version < '3.12'").evaluate()