44
55from __future__ import annotations
66
7- import functools
87import operator
98import os
109import platform
1615from ._tokenizer import ParserSyntaxError
1716from .specifiers import InvalidSpecifier , Specifier
1817from .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
205205def _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
236263def format_full_version (info : sys ._version_info ) -> str :
237264 version = f"{ info .major } .{ info .minor } .{ info .micro } "
0 commit comments