Skip to content

Commit 8a82eb2

Browse files
Ignore certain exceptions during marker evaluation
1 parent 9167919 commit 8a82eb2

File tree

2 files changed

+50
-9
lines changed

2 files changed

+50
-9
lines changed

src/packaging/markers.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from __future__ import annotations
66

7-
import functools
87
import operator
98
import os
109
import platform
@@ -16,6 +15,7 @@
1615
from ._tokenizer import ParserSyntaxError
1716
from .specifiers import InvalidSpecifier, Specifier
1817
from .utils import canonicalize_name
18+
from .version import InvalidVersion
1919

2020
__all__ = [
2121
"InvalidMarker",
@@ -203,14 +203,13 @@ def _normalize(*values: str, key: str) -> tuple[str, ...]:
203203

204204

205205
def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool:
206-
# Lazy evaluation to mitigate https://github.com/pypa/packaging/issues/774
207-
groups: list[list[Callable[[], bool]]] = [[]]
206+
groups: list[list[bool | Exception]] = [[]]
208207

209208
for marker in markers:
210209
assert isinstance(marker, (list, tuple, str))
211210

212211
if isinstance(marker, list):
213-
groups[-1].append(functools.partial(_evaluate_markers, marker, environment))
212+
groups[-1].append(_evaluate_markers(marker, environment))
214213
elif isinstance(marker, tuple):
215214
lhs, op, rhs = marker
216215

@@ -224,14 +223,42 @@ def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool:
224223
rhs_value = environment[environment_key]
225224

226225
lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
227-
groups[-1].append(functools.partial(_eval_op, lhs_value, op, rhs_value))
226+
227+
# Defer handling certain exceptions for cases where the marker expression is
228+
# otherwise well formed and they do not end up affecting the overall result.
229+
op_result: bool | Exception
230+
try:
231+
op_result = _eval_op(lhs_value, op, rhs_value)
232+
# Version comparisons may be overly strict despite being guarded against.
233+
# https://github.com/pypa/packaging/issues/774
234+
except InvalidVersion as e:
235+
op_result = e
236+
237+
groups[-1].append(op_result)
228238
else:
229239
assert marker in ["and", "or"]
230240
if marker == "or":
231241
groups.append([])
232242

233-
return any(all(expr() for expr in group) for group in groups)
234-
243+
# The below is almost equivalent to `any(all(group) for group in groups)` except
244+
# that exceptions are treated as an indeterminate logic level between true and false
245+
any_result: bool | Exception = False
246+
for group in groups:
247+
all_result: bool | Exception = True
248+
for op_result in group:
249+
# Value precedence for `all()` is: `False`, `Exception()`, `True`
250+
if (all_result is True) or (op_result is False):
251+
all_result = op_result
252+
253+
# Value precedence for `any()` is: `True`, `Exception()`, `False`
254+
if (any_result is False) or (all_result is True):
255+
any_result = all_result
256+
257+
# Raise if the overall result is indeterminate due to a expression that errored out
258+
if isinstance(any_result, Exception):
259+
raise any_result
260+
261+
return any_result
235262

236263
def format_full_version(info: sys._version_info) -> str:
237264
version = f"{info.major}.{info.minor}.{info.micro}"

tests/test_markers.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
default_environment,
2121
format_full_version,
2222
)
23+
from packaging.version import InvalidVersion
2324

2425
VARIABLES = [
2526
"extra",
@@ -321,12 +322,25 @@ def test_environment_with_extra_none(self):
321322
"sys_platform == 'foo_os' and platform_release >= '4.5.6'",
322323
{"sys_platform": "bar_os", "platform_release": "1.2.3-invalid"},
323324
False,
324-
)
325+
),
326+
(
327+
"platform_release >= '4.5.6' and sys_platform == 'foo_os'",
328+
{"sys_platform": "bar_os", "platform_release": "1.2.3-invalid"},
329+
False,
330+
),
331+
(
332+
"platform_release >= '4.5.6'",
333+
{"platform_release": "1.2.3-invalid"},
334+
InvalidVersion,
335+
),
325336
],
326337
)
327338
def test_evaluates(self, marker_string, environment, expected):
328339
args = [] if environment is None else [environment]
329-
assert Marker(marker_string).evaluate(*args) == expected
340+
try:
341+
assert Marker(marker_string).evaluate(*args) == expected
342+
except Exception as e:
343+
assert isinstance(e, expected)
330344

331345
@pytest.mark.parametrize(
332346
"marker_string",

0 commit comments

Comments
 (0)