]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/packaging/tags.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / pip / _vendor / packaging / tags.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 logging
6 import platform
7 import sys
8 import sysconfig
9 from importlib.machinery import EXTENSION_SUFFIXES
10 from typing import (
11 Dict,
12 FrozenSet,
13 Iterable,
14 Iterator,
15 List,
16 Optional,
17 Sequence,
18 Tuple,
19 Union,
20 cast,
21 )
22
23 from . import _manylinux, _musllinux
24
25 logger = logging.getLogger(__name__)
26
27 PythonVersion = Sequence[int]
28 MacVersion = Tuple[int, int]
29
30 INTERPRETER_SHORT_NAMES: Dict[str, str] = {
31 "python": "py", # Generic.
32 "cpython": "cp",
33 "pypy": "pp",
34 "ironpython": "ip",
35 "jython": "jy",
36 }
37
38
39 _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
40
41
42 class Tag:
43 """
44 A representation of the tag triple for a wheel.
45
46 Instances are considered immutable and thus are hashable. Equality checking
47 is also supported.
48 """
49
50 __slots__ = ["_interpreter", "_abi", "_platform", "_hash"]
51
52 def __init__(self, interpreter: str, abi: str, platform: str) -> None:
53 self._interpreter = interpreter.lower()
54 self._abi = abi.lower()
55 self._platform = platform.lower()
56 # The __hash__ of every single element in a Set[Tag] will be evaluated each time
57 # that a set calls its `.disjoint()` method, which may be called hundreds of
58 # times when scanning a page of links for packages with tags matching that
59 # Set[Tag]. Pre-computing the value here produces significant speedups for
60 # downstream consumers.
61 self._hash = hash((self._interpreter, self._abi, self._platform))
62
63 @property
64 def interpreter(self) -> str:
65 return self._interpreter
66
67 @property
68 def abi(self) -> str:
69 return self._abi
70
71 @property
72 def platform(self) -> str:
73 return self._platform
74
75 def __eq__(self, other: object) -> bool:
76 if not isinstance(other, Tag):
77 return NotImplemented
78
79 return (
80 (self._hash == other._hash) # Short-circuit ASAP for perf reasons.
81 and (self._platform == other._platform)
82 and (self._abi == other._abi)
83 and (self._interpreter == other._interpreter)
84 )
85
86 def __hash__(self) -> int:
87 return self._hash
88
89 def __str__(self) -> str:
90 return f"{self._interpreter}-{self._abi}-{self._platform}"
91
92 def __repr__(self) -> str:
93 return f"<{self} @ {id(self)}>"
94
95
96 def parse_tag(tag: str) -> FrozenSet[Tag]:
97 """
98 Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
99
100 Returning a set is required due to the possibility that the tag is a
101 compressed tag set.
102 """
103 tags = set()
104 interpreters, abis, platforms = tag.split("-")
105 for interpreter in interpreters.split("."):
106 for abi in abis.split("."):
107 for platform_ in platforms.split("."):
108 tags.add(Tag(interpreter, abi, platform_))
109 return frozenset(tags)
110
111
112 def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]:
113 value = sysconfig.get_config_var(name)
114 if value is None and warn:
115 logger.debug(
116 "Config variable '%s' is unset, Python ABI tag may be incorrect", name
117 )
118 return value
119
120
121 def _normalize_string(string: str) -> str:
122 return string.replace(".", "_").replace("-", "_")
123
124
125 def _abi3_applies(python_version: PythonVersion) -> bool:
126 """
127 Determine if the Python version supports abi3.
128
129 PEP 384 was first implemented in Python 3.2.
130 """
131 return len(python_version) > 1 and tuple(python_version) >= (3, 2)
132
133
134 def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
135 py_version = tuple(py_version) # To allow for version comparison.
136 abis = []
137 version = _version_nodot(py_version[:2])
138 debug = pymalloc = ucs4 = ""
139 with_debug = _get_config_var("Py_DEBUG", warn)
140 has_refcount = hasattr(sys, "gettotalrefcount")
141 # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
142 # extension modules is the best option.
143 # https://github.com/pypa/pip/issues/3383#issuecomment-173267692
144 has_ext = "_d.pyd" in EXTENSION_SUFFIXES
145 if with_debug or (with_debug is None and (has_refcount or has_ext)):
146 debug = "d"
147 if py_version < (3, 8):
148 with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
149 if with_pymalloc or with_pymalloc is None:
150 pymalloc = "m"
151 if py_version < (3, 3):
152 unicode_size = _get_config_var("Py_UNICODE_SIZE", warn)
153 if unicode_size == 4 or (
154 unicode_size is None and sys.maxunicode == 0x10FFFF
155 ):
156 ucs4 = "u"
157 elif debug:
158 # Debug builds can also load "normal" extension modules.
159 # We can also assume no UCS-4 or pymalloc requirement.
160 abis.append(f"cp{version}")
161 abis.insert(
162 0,
163 "cp{version}{debug}{pymalloc}{ucs4}".format(
164 version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4
165 ),
166 )
167 return abis
168
169
170 def cpython_tags(
171 python_version: Optional[PythonVersion] = None,
172 abis: Optional[Iterable[str]] = None,
173 platforms: Optional[Iterable[str]] = None,
174 *,
175 warn: bool = False,
176 ) -> Iterator[Tag]:
177 """
178 Yields the tags for a CPython interpreter.
179
180 The tags consist of:
181 - cp<python_version>-<abi>-<platform>
182 - cp<python_version>-abi3-<platform>
183 - cp<python_version>-none-<platform>
184 - cp<less than python_version>-abi3-<platform> # Older Python versions down to 3.2.
185
186 If python_version only specifies a major version then user-provided ABIs and
187 the 'none' ABItag will be used.
188
189 If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
190 their normal position and not at the beginning.
191 """
192 if not python_version:
193 python_version = sys.version_info[:2]
194
195 interpreter = f"cp{_version_nodot(python_version[:2])}"
196
197 if abis is None:
198 if len(python_version) > 1:
199 abis = _cpython_abis(python_version, warn)
200 else:
201 abis = []
202 abis = list(abis)
203 # 'abi3' and 'none' are explicitly handled later.
204 for explicit_abi in ("abi3", "none"):
205 try:
206 abis.remove(explicit_abi)
207 except ValueError:
208 pass
209
210 platforms = list(platforms or platform_tags())
211 for abi in abis:
212 for platform_ in platforms:
213 yield Tag(interpreter, abi, platform_)
214 if _abi3_applies(python_version):
215 yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
216 yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
217
218 if _abi3_applies(python_version):
219 for minor_version in range(python_version[1] - 1, 1, -1):
220 for platform_ in platforms:
221 interpreter = "cp{version}".format(
222 version=_version_nodot((python_version[0], minor_version))
223 )
224 yield Tag(interpreter, "abi3", platform_)
225
226
227 def _generic_abi() -> Iterator[str]:
228 abi = sysconfig.get_config_var("SOABI")
229 if abi:
230 yield _normalize_string(abi)
231
232
233 def generic_tags(
234 interpreter: Optional[str] = None,
235 abis: Optional[Iterable[str]] = None,
236 platforms: Optional[Iterable[str]] = None,
237 *,
238 warn: bool = False,
239 ) -> Iterator[Tag]:
240 """
241 Yields the tags for a generic interpreter.
242
243 The tags consist of:
244 - <interpreter>-<abi>-<platform>
245
246 The "none" ABI will be added if it was not explicitly provided.
247 """
248 if not interpreter:
249 interp_name = interpreter_name()
250 interp_version = interpreter_version(warn=warn)
251 interpreter = "".join([interp_name, interp_version])
252 if abis is None:
253 abis = _generic_abi()
254 platforms = list(platforms or platform_tags())
255 abis = list(abis)
256 if "none" not in abis:
257 abis.append("none")
258 for abi in abis:
259 for platform_ in platforms:
260 yield Tag(interpreter, abi, platform_)
261
262
263 def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
264 """
265 Yields Python versions in descending order.
266
267 After the latest version, the major-only version will be yielded, and then
268 all previous versions of that major version.
269 """
270 if len(py_version) > 1:
271 yield f"py{_version_nodot(py_version[:2])}"
272 yield f"py{py_version[0]}"
273 if len(py_version) > 1:
274 for minor in range(py_version[1] - 1, -1, -1):
275 yield f"py{_version_nodot((py_version[0], minor))}"
276
277
278 def compatible_tags(
279 python_version: Optional[PythonVersion] = None,
280 interpreter: Optional[str] = None,
281 platforms: Optional[Iterable[str]] = None,
282 ) -> Iterator[Tag]:
283 """
284 Yields the sequence of tags that are compatible with a specific version of Python.
285
286 The tags consist of:
287 - py*-none-<platform>
288 - <interpreter>-none-any # ... if `interpreter` is provided.
289 - py*-none-any
290 """
291 if not python_version:
292 python_version = sys.version_info[:2]
293 platforms = list(platforms or platform_tags())
294 for version in _py_interpreter_range(python_version):
295 for platform_ in platforms:
296 yield Tag(version, "none", platform_)
297 if interpreter:
298 yield Tag(interpreter, "none", "any")
299 for version in _py_interpreter_range(python_version):
300 yield Tag(version, "none", "any")
301
302
303 def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
304 if not is_32bit:
305 return arch
306
307 if arch.startswith("ppc"):
308 return "ppc"
309
310 return "i386"
311
312
313 def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
314 formats = [cpu_arch]
315 if cpu_arch == "x86_64":
316 if version < (10, 4):
317 return []
318 formats.extend(["intel", "fat64", "fat32"])
319
320 elif cpu_arch == "i386":
321 if version < (10, 4):
322 return []
323 formats.extend(["intel", "fat32", "fat"])
324
325 elif cpu_arch == "ppc64":
326 # TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
327 if version > (10, 5) or version < (10, 4):
328 return []
329 formats.append("fat64")
330
331 elif cpu_arch == "ppc":
332 if version > (10, 6):
333 return []
334 formats.extend(["fat32", "fat"])
335
336 if cpu_arch in {"arm64", "x86_64"}:
337 formats.append("universal2")
338
339 if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}:
340 formats.append("universal")
341
342 return formats
343
344
345 def mac_platforms(
346 version: Optional[MacVersion] = None, arch: Optional[str] = None
347 ) -> Iterator[str]:
348 """
349 Yields the platform tags for a macOS system.
350
351 The `version` parameter is a two-item tuple specifying the macOS version to
352 generate platform tags for. The `arch` parameter is the CPU architecture to
353 generate platform tags for. Both parameters default to the appropriate value
354 for the current system.
355 """
356 version_str, _, cpu_arch = platform.mac_ver()
357 if version is None:
358 version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
359 else:
360 version = version
361 if arch is None:
362 arch = _mac_arch(cpu_arch)
363 else:
364 arch = arch
365
366 if (10, 0) <= version and version < (11, 0):
367 # Prior to Mac OS 11, each yearly release of Mac OS bumped the
368 # "minor" version number. The major version was always 10.
369 for minor_version in range(version[1], -1, -1):
370 compat_version = 10, minor_version
371 binary_formats = _mac_binary_formats(compat_version, arch)
372 for binary_format in binary_formats:
373 yield "macosx_{major}_{minor}_{binary_format}".format(
374 major=10, minor=minor_version, binary_format=binary_format
375 )
376
377 if version >= (11, 0):
378 # Starting with Mac OS 11, each yearly release bumps the major version
379 # number. The minor versions are now the midyear updates.
380 for major_version in range(version[0], 10, -1):
381 compat_version = major_version, 0
382 binary_formats = _mac_binary_formats(compat_version, arch)
383 for binary_format in binary_formats:
384 yield "macosx_{major}_{minor}_{binary_format}".format(
385 major=major_version, minor=0, binary_format=binary_format
386 )
387
388 if version >= (11, 0):
389 # Mac OS 11 on x86_64 is compatible with binaries from previous releases.
390 # Arm64 support was introduced in 11.0, so no Arm binaries from previous
391 # releases exist.
392 #
393 # However, the "universal2" binary format can have a
394 # macOS version earlier than 11.0 when the x86_64 part of the binary supports
395 # that version of macOS.
396 if arch == "x86_64":
397 for minor_version in range(16, 3, -1):
398 compat_version = 10, minor_version
399 binary_formats = _mac_binary_formats(compat_version, arch)
400 for binary_format in binary_formats:
401 yield "macosx_{major}_{minor}_{binary_format}".format(
402 major=compat_version[0],
403 minor=compat_version[1],
404 binary_format=binary_format,
405 )
406 else:
407 for minor_version in range(16, 3, -1):
408 compat_version = 10, minor_version
409 binary_format = "universal2"
410 yield "macosx_{major}_{minor}_{binary_format}".format(
411 major=compat_version[0],
412 minor=compat_version[1],
413 binary_format=binary_format,
414 )
415
416
417 def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
418 linux = _normalize_string(sysconfig.get_platform())
419 if is_32bit:
420 if linux == "linux_x86_64":
421 linux = "linux_i686"
422 elif linux == "linux_aarch64":
423 linux = "linux_armv7l"
424 _, arch = linux.split("_", 1)
425 yield from _manylinux.platform_tags(linux, arch)
426 yield from _musllinux.platform_tags(arch)
427 yield linux
428
429
430 def _generic_platforms() -> Iterator[str]:
431 yield _normalize_string(sysconfig.get_platform())
432
433
434 def platform_tags() -> Iterator[str]:
435 """
436 Provides the platform tags for this installation.
437 """
438 if platform.system() == "Darwin":
439 return mac_platforms()
440 elif platform.system() == "Linux":
441 return _linux_platforms()
442 else:
443 return _generic_platforms()
444
445
446 def interpreter_name() -> str:
447 """
448 Returns the name of the running interpreter.
449 """
450 name = sys.implementation.name
451 return INTERPRETER_SHORT_NAMES.get(name) or name
452
453
454 def interpreter_version(*, warn: bool = False) -> str:
455 """
456 Returns the version of the running interpreter.
457 """
458 version = _get_config_var("py_version_nodot", warn=warn)
459 if version:
460 version = str(version)
461 else:
462 version = _version_nodot(sys.version_info[:2])
463 return version
464
465
466 def _version_nodot(version: PythonVersion) -> str:
467 return "".join(map(str, version))
468
469
470 def sys_tags(*, warn: bool = False) -> Iterator[Tag]:
471 """
472 Returns the sequence of tag triples for the running interpreter.
473
474 The order of the sequence corresponds to priority order for the
475 interpreter, from most to least important.
476 """
477
478 interp_name = interpreter_name()
479 if interp_name == "cp":
480 yield from cpython_tags(warn=warn)
481 else:
482 yield from generic_tags()
483
484 if interp_name == "pp":
485 yield from compatible_tags(interpreter="pp3")
486 else:
487 yield from compatible_tags()