Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/packaging/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ class UndefinedComparison(ValueError):
"""


class UndefinedEnvironmentName(ValueError):
"""Raised when evaluating a marker that references a missing environment key."""
class UndefinedEnvironmentName(KeyError):
"""Raised when evaluating a marker that references a missing environment key.

Subclasses :class:`KeyError` so that code catching the bare ``KeyError`` that
a missing environment lookup historically produced keeps working.
"""


class Environment(TypedDict):
Expand Down Expand Up @@ -257,6 +261,15 @@ def _normalize(
return lhs, rhs


def _lookup_environment(
environment: dict[str, str | AbstractSet[str]], key: str
) -> str | AbstractSet[str]:
try:
return environment[key]
except KeyError:
raise UndefinedEnvironmentName(key) from None


def _evaluate_markers(
markers: MarkerList, environment: dict[str, str | AbstractSet[str]]
) -> bool:
Expand All @@ -270,12 +283,12 @@ def _evaluate_markers(

if isinstance(lhs, Variable):
environment_key = lhs.value
lhs_value = environment[environment_key]
lhs_value = _lookup_environment(environment, environment_key)
rhs_value = rhs.value
else:
lhs_value = lhs.value
environment_key = rhs.value
rhs_value = environment[environment_key]
rhs_value = _lookup_environment(environment, environment_key)

assert isinstance(lhs_value, str), "lhs must be a string"
lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
Expand Down
12 changes: 12 additions & 0 deletions tests/test_markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
InvalidMarker,
Marker,
UndefinedComparison,
UndefinedEnvironmentName,
_format_full_version,
default_environment,
)
Expand Down Expand Up @@ -508,6 +509,17 @@ def test_extras_and_dependency_groups_disallowed(self, variable: str) -> None:
with pytest.raises(KeyError):
marker.evaluate(context="requirement")

def test_missing_environment_key_raises_undefined_environment_name(self) -> None:
marker = Marker('"foo" in extras')
with pytest.raises(UndefinedEnvironmentName) as ctx:
marker.evaluate()
assert ctx.value.args == ("extras",)
# UndefinedEnvironmentName subclasses KeyError, so the historical
# bare ``except KeyError`` for a missing environment key still works.
assert issubclass(UndefinedEnvironmentName, KeyError)
with pytest.raises(KeyError):
marker.evaluate()

@pytest.mark.parametrize(
("marker_string", "environment", "expected"),
[
Expand Down
Loading