comparison planemo/lib/python3.7/site-packages/packaging/markers.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
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 from __future__ import absolute_import, division, print_function
5
6 import operator
7 import os
8 import platform
9 import sys
10
11 from pyparsing import ParseException, ParseResults, stringStart, stringEnd
12 from pyparsing import ZeroOrMore, Group, Forward, QuotedString
13 from pyparsing import Literal as L # noqa
14
15 from ._compat import string_types
16 from ._typing import TYPE_CHECKING
17 from .specifiers import Specifier, InvalidSpecifier
18
19 if TYPE_CHECKING: # pragma: no cover
20 from typing import Any, Callable, Dict, List, Optional, Tuple, Union
21
22 Operator = Callable[[str, str], bool]
23
24
25 __all__ = [
26 "InvalidMarker",
27 "UndefinedComparison",
28 "UndefinedEnvironmentName",
29 "Marker",
30 "default_environment",
31 ]
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 class Node(object):
54 def __init__(self, value):
55 # type: (Any) -> None
56 self.value = value
57
58 def __str__(self):
59 # type: () -> str
60 return str(self.value)
61
62 def __repr__(self):
63 # type: () -> str
64 return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
65
66 def serialize(self):
67 # type: () -> str
68 raise NotImplementedError
69
70
71 class Variable(Node):
72 def serialize(self):
73 # type: () -> str
74 return str(self)
75
76
77 class Value(Node):
78 def serialize(self):
79 # type: () -> str
80 return '"{0}"'.format(self)
81
82
83 class Op(Node):
84 def serialize(self):
85 # type: () -> str
86 return str(self)
87
88
89 VARIABLE = (
90 L("implementation_version")
91 | L("platform_python_implementation")
92 | L("implementation_name")
93 | L("python_full_version")
94 | L("platform_release")
95 | L("platform_version")
96 | L("platform_machine")
97 | L("platform_system")
98 | L("python_version")
99 | L("sys_platform")
100 | L("os_name")
101 | L("os.name") # PEP-345
102 | L("sys.platform") # PEP-345
103 | L("platform.version") # PEP-345
104 | L("platform.machine") # PEP-345
105 | L("platform.python_implementation") # PEP-345
106 | L("python_implementation") # undocumented setuptools legacy
107 | L("extra") # PEP-508
108 )
109 ALIASES = {
110 "os.name": "os_name",
111 "sys.platform": "sys_platform",
112 "platform.version": "platform_version",
113 "platform.machine": "platform_machine",
114 "platform.python_implementation": "platform_python_implementation",
115 "python_implementation": "platform_python_implementation",
116 }
117 VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
118
119 VERSION_CMP = (
120 L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<")
121 )
122
123 MARKER_OP = VERSION_CMP | L("not in") | L("in")
124 MARKER_OP.setParseAction(lambda s, l, t: Op(t[0]))
125
126 MARKER_VALUE = QuotedString("'") | QuotedString('"')
127 MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0]))
128
129 BOOLOP = L("and") | L("or")
130
131 MARKER_VAR = VARIABLE | MARKER_VALUE
132
133 MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR)
134 MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0]))
135
136 LPAREN = L("(").suppress()
137 RPAREN = L(")").suppress()
138
139 MARKER_EXPR = Forward()
140 MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN)
141 MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR)
142
143 MARKER = stringStart + MARKER_EXPR + stringEnd
144
145
146 def _coerce_parse_result(results):
147 # type: (Union[ParseResults, List[Any]]) -> List[Any]
148 if isinstance(results, ParseResults):
149 return [_coerce_parse_result(i) for i in results]
150 else:
151 return results
152
153
154 def _format_marker(marker, first=True):
155 # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
156
157 assert isinstance(marker, (list, tuple, string_types))
158
159 # Sometimes we have a structure like [[...]] which is a single item list
160 # where the single item is itself it's own list. In that case we want skip
161 # the rest of this function so that we don't get extraneous () on the
162 # outside.
163 if (
164 isinstance(marker, list)
165 and len(marker) == 1
166 and isinstance(marker[0], (list, tuple))
167 ):
168 return _format_marker(marker[0])
169
170 if isinstance(marker, list):
171 inner = (_format_marker(m, first=False) for m in marker)
172 if first:
173 return " ".join(inner)
174 else:
175 return "(" + " ".join(inner) + ")"
176 elif isinstance(marker, tuple):
177 return " ".join([m.serialize() for m in marker])
178 else:
179 return marker
180
181
182 _operators = {
183 "in": lambda lhs, rhs: lhs in rhs,
184 "not in": lambda lhs, rhs: lhs not in rhs,
185 "<": operator.lt,
186 "<=": operator.le,
187 "==": operator.eq,
188 "!=": operator.ne,
189 ">=": operator.ge,
190 ">": operator.gt,
191 } # type: Dict[str, Operator]
192
193
194 def _eval_op(lhs, op, rhs):
195 # type: (str, Op, str) -> bool
196 try:
197 spec = Specifier("".join([op.serialize(), rhs]))
198 except InvalidSpecifier:
199 pass
200 else:
201 return spec.contains(lhs)
202
203 oper = _operators.get(op.serialize()) # type: Optional[Operator]
204 if oper is None:
205 raise UndefinedComparison(
206 "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
207 )
208
209 return oper(lhs, rhs)
210
211
212 class Undefined(object):
213 pass
214
215
216 _undefined = Undefined()
217
218
219 def _get_env(environment, name):
220 # type: (Dict[str, str], str) -> str
221 value = environment.get(name, _undefined) # type: Union[str, Undefined]
222
223 if isinstance(value, Undefined):
224 raise UndefinedEnvironmentName(
225 "{0!r} does not exist in evaluation environment.".format(name)
226 )
227
228 return value
229
230
231 def _evaluate_markers(markers, environment):
232 # type: (List[Any], Dict[str, str]) -> bool
233 groups = [[]] # type: List[List[bool]]
234
235 for marker in markers:
236 assert isinstance(marker, (list, tuple, string_types))
237
238 if isinstance(marker, list):
239 groups[-1].append(_evaluate_markers(marker, environment))
240 elif isinstance(marker, tuple):
241 lhs, op, rhs = marker
242
243 if isinstance(lhs, Variable):
244 lhs_value = _get_env(environment, lhs.value)
245 rhs_value = rhs.value
246 else:
247 lhs_value = lhs.value
248 rhs_value = _get_env(environment, rhs.value)
249
250 groups[-1].append(_eval_op(lhs_value, op, rhs_value))
251 else:
252 assert marker in ["and", "or"]
253 if marker == "or":
254 groups.append([])
255
256 return any(all(item) for item in groups)
257
258
259 def format_full_version(info):
260 # type: (sys._version_info) -> str
261 version = "{0.major}.{0.minor}.{0.micro}".format(info)
262 kind = info.releaselevel
263 if kind != "final":
264 version += kind[0] + str(info.serial)
265 return version
266
267
268 def default_environment():
269 # type: () -> Dict[str, str]
270 if hasattr(sys, "implementation"):
271 # Ignoring the `sys.implementation` reference for type checking due to
272 # mypy not liking that the attribute doesn't exist in Python 2.7 when
273 # run with the `--py27` flag.
274 iver = format_full_version(sys.implementation.version) # type: ignore
275 implementation_name = sys.implementation.name # type: ignore
276 else:
277 iver = "0"
278 implementation_name = ""
279
280 return {
281 "implementation_name": implementation_name,
282 "implementation_version": iver,
283 "os_name": os.name,
284 "platform_machine": platform.machine(),
285 "platform_release": platform.release(),
286 "platform_system": platform.system(),
287 "platform_version": platform.version(),
288 "python_full_version": platform.python_version(),
289 "platform_python_implementation": platform.python_implementation(),
290 "python_version": ".".join(platform.python_version_tuple()[:2]),
291 "sys_platform": sys.platform,
292 }
293
294
295 class Marker(object):
296 def __init__(self, marker):
297 # type: (str) -> None
298 try:
299 self._markers = _coerce_parse_result(MARKER.parseString(marker))
300 except ParseException as e:
301 err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
302 marker, marker[e.loc : e.loc + 8]
303 )
304 raise InvalidMarker(err_str)
305
306 def __str__(self):
307 # type: () -> str
308 return _format_marker(self._markers)
309
310 def __repr__(self):
311 # type: () -> str
312 return "<Marker({0!r})>".format(str(self))
313
314 def evaluate(self, environment=None):
315 # type: (Optional[Dict[str, str]]) -> bool
316 """Evaluate a marker.
317
318 Return the boolean from evaluating the given marker against the
319 environment. environment is an optional argument to override all or
320 part of the determined environment.
321
322 The environment is determined from the current Python process.
323 """
324 current_environment = default_environment()
325 if environment is not None:
326 current_environment.update(environment)
327
328 return _evaluate_markers(self._markers, current_environment)