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.
9 from importlib
.machinery
import EXTENSION_SUFFIXES
23 from . import _manylinux
, _musllinux
25 logger
= logging
.getLogger(__name__
)
27 PythonVersion
= Sequence
[int]
28 MacVersion
= Tuple
[int, int]
30 INTERPRETER_SHORT_NAMES
: Dict
[str, str] = {
31 "python": "py", # Generic.
39 _32_BIT_INTERPRETER
= sys
.maxsize
<= 2 ** 32
44 A representation of the tag triple for a wheel.
46 Instances are considered immutable and thus are hashable. Equality checking
50 __slots__
= ["_interpreter", "_abi", "_platform", "_hash"]
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
))
64 def interpreter(self
) -> str:
65 return self
._interpreter
72 def platform(self
) -> str:
75 def __eq__(self
, other
: object) -> bool:
76 if not isinstance(other
, Tag
):
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
)
86 def __hash__(self
) -> int:
89 def __str__(self
) -> str:
90 return f
"{self._interpreter}-{self._abi}-{self._platform}"
92 def __repr__(self
) -> str:
93 return f
"<{self} @ {id(self)}>"
96 def parse_tag(tag
: str) -> FrozenSet
[Tag
]:
98 Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
100 Returning a set is required due to the possibility that the tag is a
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
)
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
:
116 "Config variable '%s' is unset, Python ABI tag may be incorrect", name
121 def _normalize_string(string
: str) -> str:
122 return string
.replace(".", "_").replace("-", "_")
125 def _abi3_applies(python_version
: PythonVersion
) -> bool:
127 Determine if the Python version supports abi3.
129 PEP 384 was first implemented in Python 3.2.
131 return len(python_version
) > 1 and tuple(python_version
) >= (3, 2)
134 def _cpython_abis(py_version
: PythonVersion
, warn
: bool = False) -> List
[str]:
135 py_version
= tuple(py_version
) # To allow for version comparison.
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
)):
147 if py_version
< (3, 8):
148 with_pymalloc
= _get_config_var("WITH_PYMALLOC", warn
)
149 if with_pymalloc
or with_pymalloc
is None:
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
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}")
163 "cp{version}{debug}{pymalloc}{ucs4}".format(
164 version
=version
, debug
=debug
, pymalloc
=pymalloc
, ucs4
=ucs4
171 python_version
: Optional
[PythonVersion
] = None,
172 abis
: Optional
[Iterable
[str]] = None,
173 platforms
: Optional
[Iterable
[str]] = None,
178 Yields the tags for a CPython interpreter.
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.
186 If python_version only specifies a major version then user-provided ABIs and
187 the 'none' ABItag will be used.
189 If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
190 their normal position and not at the beginning.
192 if not python_version
:
193 python_version
= sys
.version_info
[:2]
195 interpreter
= f
"cp{_version_nodot(python_version[:2])}"
198 if len(python_version
) > 1:
199 abis
= _cpython_abis(python_version
, warn
)
203 # 'abi3' and 'none' are explicitly handled later.
204 for explicit_abi
in ("abi3", "none"):
206 abis
.remove(explicit_abi
)
210 platforms
= list(platforms
or platform_tags())
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
)
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
))
224 yield Tag(interpreter
, "abi3", platform_
)
227 def _generic_abi() -> Iterator
[str]:
228 abi
= sysconfig
.get_config_var("SOABI")
230 yield _normalize_string(abi
)
234 interpreter
: Optional
[str] = None,
235 abis
: Optional
[Iterable
[str]] = None,
236 platforms
: Optional
[Iterable
[str]] = None,
241 Yields the tags for a generic interpreter.
244 - <interpreter>-<abi>-<platform>
246 The "none" ABI will be added if it was not explicitly provided.
249 interp_name
= interpreter_name()
250 interp_version
= interpreter_version(warn
=warn
)
251 interpreter
= "".join([interp_name
, interp_version
])
253 abis
= _generic_abi()
254 platforms
= list(platforms
or platform_tags())
256 if "none" not in abis
:
259 for platform_
in platforms
:
260 yield Tag(interpreter
, abi
, platform_
)
263 def _py_interpreter_range(py_version
: PythonVersion
) -> Iterator
[str]:
265 Yields Python versions in descending order.
267 After the latest version, the major-only version will be yielded, and then
268 all previous versions of that major version.
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))}"
279 python_version
: Optional
[PythonVersion
] = None,
280 interpreter
: Optional
[str] = None,
281 platforms
: Optional
[Iterable
[str]] = None,
284 Yields the sequence of tags that are compatible with a specific version of Python.
287 - py*-none-<platform>
288 - <interpreter>-none-any # ... if `interpreter` is provided.
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_
)
298 yield Tag(interpreter
, "none", "any")
299 for version
in _py_interpreter_range(python_version
):
300 yield Tag(version
, "none", "any")
303 def _mac_arch(arch
: str, is_32bit
: bool = _32_BIT_INTERPRETER
) -> str:
307 if arch
.startswith("ppc"):
313 def _mac_binary_formats(version
: MacVersion
, cpu_arch
: str) -> List
[str]:
315 if cpu_arch
== "x86_64":
316 if version
< (10, 4):
318 formats
.extend(["intel", "fat64", "fat32"])
320 elif cpu_arch
== "i386":
321 if version
< (10, 4):
323 formats
.extend(["intel", "fat32", "fat"])
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):
329 formats
.append("fat64")
331 elif cpu_arch
== "ppc":
332 if version
> (10, 6):
334 formats
.extend(["fat32", "fat"])
336 if cpu_arch
in {"arm64", "x86_64"}
:
337 formats
.append("universal2")
339 if cpu_arch
in {"x86_64", "i386", "ppc64", "ppc", "intel"}
:
340 formats
.append("universal")
346 version
: Optional
[MacVersion
] = None, arch
: Optional
[str] = None
349 Yields the platform tags for a macOS system.
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.
356 version_str
, _
, cpu_arch
= platform
.mac_ver()
358 version
= cast("MacVersion", tuple(map(int, version_str
.split(".")[:2])))
362 arch
= _mac_arch(cpu_arch
)
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
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
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
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.
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
,
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
,
417 def _linux_platforms(is_32bit
: bool = _32_BIT_INTERPRETER
) -> Iterator
[str]:
418 linux
= _normalize_string(sysconfig
.get_platform())
420 if linux
== "linux_x86_64":
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
)
430 def _generic_platforms() -> Iterator
[str]:
431 yield _normalize_string(sysconfig
.get_platform())
434 def platform_tags() -> Iterator
[str]:
436 Provides the platform tags for this installation.
438 if platform
.system() == "Darwin":
439 return mac_platforms()
440 elif platform
.system() == "Linux":
441 return _linux_platforms()
443 return _generic_platforms()
446 def interpreter_name() -> str:
448 Returns the name of the running interpreter.
450 name
= sys
.implementation
.name
451 return INTERPRETER_SHORT_NAMES
.get(name
) or name
454 def interpreter_version(*, warn
: bool = False) -> str:
456 Returns the version of the running interpreter.
458 version
= _get_config_var("py_version_nodot", warn
=warn
)
460 version
= str(version
)
462 version
= _version_nodot(sys
.version_info
[:2])
466 def _version_nodot(version
: PythonVersion
) -> str:
467 return "".join(map(str, version
))
470 def sys_tags(*, warn
: bool = False) -> Iterator
[Tag
]:
472 Returns the sequence of tag triples for the running interpreter.
474 The order of the sequence corresponds to priority order for the
475 interpreter, from most to least important.
478 interp_name
= interpreter_name()
479 if interp_name
== "cp":
480 yield from cpython_tags(warn
=warn
)
482 yield from generic_tags()
484 if interp_name
== "pp":
485 yield from compatible_tags(interpreter
="pp3")
487 yield from compatible_tags()