]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/markers.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / setuptools / _vendor / packaging / markers.py
1 # This file is dual licensed under the terms of the Apache License, Version
2 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3 # for complete details.
4
5 import operator
6 import os
7 import platform
8 import sys
9 from typing import Any, Callable, Dict, List, Optional, Tuple, Union
10
11 from ._parser import (
12 MarkerAtom,
13 MarkerList,
14 Op,
15 Value,
16 Variable,
17 parse_marker as _parse_marker,
18 )
19 from ._tokenizer import ParserSyntaxError
20 from .specifiers import InvalidSpecifier, Specifier
21 from .utils import canonicalize_name
22
23 __all__ = [
24 "InvalidMarker",
25 "UndefinedComparison",
26 "UndefinedEnvironmentName",
27 "Marker",
28 "default_environment",
29 ]
30
31 Operator = Callable[[str, str], bool]
32
33
34 class InvalidMarker(ValueError):
35 """
36 An invalid marker was found, users should refer to PEP 508.
37 """
38
39
40 class UndefinedComparison(ValueError):
41 """
42 An invalid operation was attempted on a value that doesn't support it.
43 """
44
45
46 class UndefinedEnvironmentName(ValueError):
47 """
48 A name was attempted to be used that does not exist inside of the
49 environment.
50 """
51
52
53 def _normalize_extra_values(results: Any) -> Any:
54 """
55 Normalize extra values.
56 """
57 if isinstance(results[0], tuple):
58 lhs, op, rhs = results[0]
59 if isinstance(lhs, Variable) and lhs.value == "extra":
60 normalized_extra = canonicalize_name(rhs.value)
61 rhs = Value(normalized_extra)
62 elif isinstance(rhs, Variable) and rhs.value == "extra":
63 normalized_extra = canonicalize_name(lhs.value)
64 lhs = Value(normalized_extra)
65 results[0] = lhs, op, rhs
66 return results
67
68
69 def _format_marker(
70 marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True
71 ) -> str:
72
73 assert isinstance(marker, (list, tuple, str))
74
75 # Sometimes we have a structure like [[...]] which is a single item list
76 # where the single item is itself it's own list. In that case we want skip
77 # the rest of this function so that we don't get extraneous () on the
78 # outside.
79 if (
80 isinstance(marker, list)
81 and len(marker) == 1
82 and isinstance(marker[0], (list, tuple))
83 ):
84 return _format_marker(marker[0])
85
86 if isinstance(marker, list):
87 inner = (_format_marker(m, first=False) for m in marker)
88 if first:
89 return " ".join(inner)
90 else:
91 return "(" + " ".join(inner) + ")"
92 elif isinstance(marker, tuple):
93 return " ".join([m.serialize() for m in marker])
94 else:
95 return marker
96
97
98 _operators: Dict[str, Operator] = {
99 "in": lambda lhs, rhs: lhs in rhs,
100 "not in": lambda lhs, rhs: lhs not in rhs,
101 "<": operator.lt,
102 "<=": operator.le,
103 "==": operator.eq,
104 "!=": operator.ne,
105 ">=": operator.ge,
106 ">": operator.gt,
107 }
108
109
110 def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
111 try:
112 spec = Specifier("".join([op.serialize(), rhs]))
113 except InvalidSpecifier:
114 pass
115 else:
116 return spec.contains(lhs, prereleases=True)
117
118 oper: Optional[Operator] = _operators.get(op.serialize())
119 if oper is None:
120 raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
121
122 return oper(lhs, rhs)
123
124
125 def _normalize(*values: str, key: str) -> Tuple[str, ...]:
126 # PEP 685 – Comparison of extra names for optional distribution dependencies
127 # https://peps.python.org/pep-0685/
128 # > When comparing extra names, tools MUST normalize the names being
129 # > compared using the semantics outlined in PEP 503 for names
130 if key == "extra":
131 return tuple(canonicalize_name(v) for v in values)
132
133 # other environment markers don't have such standards
134 return values
135
136
137 def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
138 groups: List[List[bool]] = [[]]
139
140 for marker in markers:
141 assert isinstance(marker, (list, tuple, str))
142
143 if isinstance(marker, list):
144 groups[-1].append(_evaluate_markers(marker, environment))
145 elif isinstance(marker, tuple):
146 lhs, op, rhs = marker
147
148 if isinstance(lhs, Variable):
149 environment_key = lhs.value
150 lhs_value = environment[environment_key]
151 rhs_value = rhs.value
152 else:
153 lhs_value = lhs.value
154 environment_key = rhs.value
155 rhs_value = environment[environment_key]
156
157 lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
158 groups[-1].append(_eval_op(lhs_value, op, rhs_value))
159 else:
160 assert marker in ["and", "or"]
161 if marker == "or":
162 groups.append([])
163
164 return any(all(item) for item in groups)
165
166
167 def format_full_version(info: "sys._version_info") -> str:
168 version = "{0.major}.{0.minor}.{0.micro}".format(info)
169 kind = info.releaselevel
170 if kind != "final":
171 version += kind[0] + str(info.serial)
172 return version
173
174
175 def default_environment() -> Dict[str, str]:
176 iver = format_full_version(sys.implementation.version)
177 implementation_name = sys.implementation.name
178 return {
179 "implementation_name": implementation_name,
180 "implementation_version": iver,
181 "os_name": os.name,
182 "platform_machine": platform.machine(),
183 "platform_release": platform.release(),
184 "platform_system": platform.system(),
185 "platform_version": platform.version(),
186 "python_full_version": platform.python_version(),
187 "platform_python_implementation": platform.python_implementation(),
188 "python_version": ".".join(platform.python_version_tuple()[:2]),
189 "sys_platform": sys.platform,
190 }
191
192
193 class Marker:
194 def __init__(self, marker: str) -> None:
195 # Note: We create a Marker object without calling this constructor in
196 # packaging.requirements.Requirement. If any additional logic is
197 # added here, make sure to mirror/adapt Requirement.
198 try:
199 self._markers = _normalize_extra_values(_parse_marker(marker))
200 # The attribute `_markers` can be described in terms of a recursive type:
201 # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]]
202 #
203 # For example, the following expression:
204 # python_version > "3.6" or (python_version == "3.6" and os_name == "unix")
205 #
206 # is parsed into:
207 # [
208 # (<Variable('python_version')>, <Op('>')>, <Value('3.6')>),
209 # 'and',
210 # [
211 # (<Variable('python_version')>, <Op('==')>, <Value('3.6')>),
212 # 'or',
213 # (<Variable('os_name')>, <Op('==')>, <Value('unix')>)
214 # ]
215 # ]
216 except ParserSyntaxError as e:
217 raise InvalidMarker(str(e)) from e
218
219 def __str__(self) -> str:
220 return _format_marker(self._markers)
221
222 def __repr__(self) -> str:
223 return f"<Marker('{self}')>"
224
225 def __hash__(self) -> int:
226 return hash((self.__class__.__name__, str(self)))
227
228 def __eq__(self, other: Any) -> bool:
229 if not isinstance(other, Marker):
230 return NotImplemented
231
232 return str(self) == str(other)
233
234 def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
235 """Evaluate a marker.
236
237 Return the boolean from evaluating the given marker against the
238 environment. environment is an optional argument to override all or
239 part of the determined environment.
240
241 The environment is determined from the current Python process.
242 """
243 current_environment = default_environment()
244 current_environment["extra"] = ""
245 if environment is not None:
246 current_environment.update(environment)
247 # The API used to allow setting extra to None. We need to handle this
248 # case for backwards compatibility.
249 if current_environment["extra"] is None:
250 current_environment["extra"] = ""
251
252 return _evaluate_markers(self._markers, current_environment)