mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2026-04-16 08:30:20 +00:00
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
This commit is contained in:
parent
72ad6dc953
commit
f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions
|
|
@ -1,26 +1,27 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
"__title__",
|
||||
"__summary__",
|
||||
"__uri__",
|
||||
"__version__",
|
||||
"__author__",
|
||||
"__email__",
|
||||
"__license__",
|
||||
"__copyright__",
|
||||
'__title__',
|
||||
'__summary__',
|
||||
'__uri__',
|
||||
'__version__',
|
||||
'__author__',
|
||||
'__email__',
|
||||
'__license__',
|
||||
'__copyright__',
|
||||
]
|
||||
|
||||
__title__ = "packaging"
|
||||
__summary__ = "Core utilities for Python packages"
|
||||
__uri__ = "https://github.com/pypa/packaging"
|
||||
__title__ = 'packaging'
|
||||
__summary__ = 'Core utilities for Python packages'
|
||||
__uri__ = 'https://github.com/pypa/packaging'
|
||||
|
||||
__version__ = "21.3"
|
||||
__version__ = '21.3'
|
||||
|
||||
__author__ = "Donald Stufft and individual contributors"
|
||||
__email__ = "donald@stufft.io"
|
||||
__author__ = 'Donald Stufft and individual contributors'
|
||||
__email__ = 'donald@stufft.io'
|
||||
|
||||
__license__ = "BSD-2-Clause or Apache-2.0"
|
||||
__copyright__ = "2014-2019 %s" % __author__
|
||||
__license__ = 'BSD-2-Clause or Apache-2.0'
|
||||
__copyright__ = '2014-2019 %s' % __author__
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
from .__about__ import (
|
||||
__author__,
|
||||
__copyright__,
|
||||
__email__,
|
||||
__license__,
|
||||
__summary__,
|
||||
__title__,
|
||||
__uri__,
|
||||
__version__,
|
||||
)
|
||||
from .__about__ import __author__
|
||||
from .__about__ import __copyright__
|
||||
from .__about__ import __email__
|
||||
from .__about__ import __license__
|
||||
from .__about__ import __summary__
|
||||
from .__about__ import __title__
|
||||
from .__about__ import __uri__
|
||||
from .__about__ import __version__
|
||||
|
||||
__all__ = [
|
||||
"__title__",
|
||||
"__summary__",
|
||||
"__uri__",
|
||||
"__version__",
|
||||
"__author__",
|
||||
"__email__",
|
||||
"__license__",
|
||||
"__copyright__",
|
||||
'__title__',
|
||||
'__summary__',
|
||||
'__uri__',
|
||||
'__version__',
|
||||
'__author__',
|
||||
'__email__',
|
||||
'__license__',
|
||||
'__copyright__',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import functools
|
||||
import os
|
||||
|
|
@ -5,7 +7,12 @@ import re
|
|||
import struct
|
||||
import sys
|
||||
import warnings
|
||||
from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple
|
||||
from typing import Dict
|
||||
from typing import IO
|
||||
from typing import Iterator
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
# Python does not provide platform information at sufficient granularity to
|
||||
|
|
@ -36,27 +43,27 @@ class _ELFFileHeader:
|
|||
def unpack(fmt: str) -> int:
|
||||
try:
|
||||
data = file.read(struct.calcsize(fmt))
|
||||
result: Tuple[int, ...] = struct.unpack(fmt, data)
|
||||
result: tuple[int, ...] = struct.unpack(fmt, data)
|
||||
except struct.error:
|
||||
raise _ELFFileHeader._InvalidELFFileHeader()
|
||||
return result[0]
|
||||
|
||||
self.e_ident_magic = unpack(">I")
|
||||
self.e_ident_magic = unpack('>I')
|
||||
if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
|
||||
raise _ELFFileHeader._InvalidELFFileHeader()
|
||||
self.e_ident_class = unpack("B")
|
||||
self.e_ident_class = unpack('B')
|
||||
if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
|
||||
raise _ELFFileHeader._InvalidELFFileHeader()
|
||||
self.e_ident_data = unpack("B")
|
||||
self.e_ident_data = unpack('B')
|
||||
if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
|
||||
raise _ELFFileHeader._InvalidELFFileHeader()
|
||||
self.e_ident_version = unpack("B")
|
||||
self.e_ident_osabi = unpack("B")
|
||||
self.e_ident_abiversion = unpack("B")
|
||||
self.e_ident_version = unpack('B')
|
||||
self.e_ident_osabi = unpack('B')
|
||||
self.e_ident_abiversion = unpack('B')
|
||||
self.e_ident_pad = file.read(7)
|
||||
format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
|
||||
format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
|
||||
format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
|
||||
format_h = '<H' if self.e_ident_data == self.ELFDATA2LSB else '>H'
|
||||
format_i = '<I' if self.e_ident_data == self.ELFDATA2LSB else '>I'
|
||||
format_q = '<Q' if self.e_ident_data == self.ELFDATA2LSB else '>Q'
|
||||
format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
|
||||
self.e_type = unpack(format_h)
|
||||
self.e_machine = unpack(format_h)
|
||||
|
|
@ -73,9 +80,9 @@ class _ELFFileHeader:
|
|||
self.e_shstrndx = unpack(format_h)
|
||||
|
||||
|
||||
def _get_elf_header() -> Optional[_ELFFileHeader]:
|
||||
def _get_elf_header() -> _ELFFileHeader | None:
|
||||
try:
|
||||
with open(sys.executable, "rb") as f:
|
||||
with open(sys.executable, 'rb') as f:
|
||||
elf_header = _ELFFileHeader(f)
|
||||
except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
|
||||
return None
|
||||
|
|
@ -112,11 +119,11 @@ def _is_linux_i686() -> bool:
|
|||
|
||||
|
||||
def _have_compatible_abi(arch: str) -> bool:
|
||||
if arch == "armv7l":
|
||||
if arch == 'armv7l':
|
||||
return _is_linux_armhf()
|
||||
if arch == "i686":
|
||||
if arch == 'i686':
|
||||
return _is_linux_i686()
|
||||
return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"}
|
||||
return arch in {'x86_64', 'aarch64', 'ppc64', 'ppc64le', 's390x'}
|
||||
|
||||
|
||||
# If glibc ever changes its major version, we need to know what the last
|
||||
|
|
@ -124,7 +131,7 @@ def _have_compatible_abi(arch: str) -> bool:
|
|||
# For now, guess what the highest minor version might be, assume it will
|
||||
# be 50 for testing. Once this actually happens, update the dictionary
|
||||
# with the actual value.
|
||||
_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50)
|
||||
_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50)
|
||||
|
||||
|
||||
class _GLibCVersion(NamedTuple):
|
||||
|
|
@ -132,7 +139,7 @@ class _GLibCVersion(NamedTuple):
|
|||
minor: int
|
||||
|
||||
|
||||
def _glibc_version_string_confstr() -> Optional[str]:
|
||||
def _glibc_version_string_confstr() -> str | None:
|
||||
"""
|
||||
Primary implementation of glibc_version_string using os.confstr.
|
||||
"""
|
||||
|
|
@ -142,7 +149,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
|
|||
# https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
|
||||
try:
|
||||
# os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
|
||||
version_string = os.confstr("CS_GNU_LIBC_VERSION")
|
||||
version_string = os.confstr('CS_GNU_LIBC_VERSION')
|
||||
assert version_string is not None
|
||||
_, version = version_string.split()
|
||||
except (AssertionError, AttributeError, OSError, ValueError):
|
||||
|
|
@ -151,7 +158,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
|
|||
return version
|
||||
|
||||
|
||||
def _glibc_version_string_ctypes() -> Optional[str]:
|
||||
def _glibc_version_string_ctypes() -> str | None:
|
||||
"""
|
||||
Fallback implementation of glibc_version_string using ctypes.
|
||||
"""
|
||||
|
|
@ -190,17 +197,17 @@ def _glibc_version_string_ctypes() -> Optional[str]:
|
|||
version_str: str = gnu_get_libc_version()
|
||||
# py2 / py3 compatibility:
|
||||
if not isinstance(version_str, str):
|
||||
version_str = version_str.decode("ascii")
|
||||
version_str = version_str.decode('ascii')
|
||||
|
||||
return version_str
|
||||
|
||||
|
||||
def _glibc_version_string() -> Optional[str]:
|
||||
def _glibc_version_string() -> str | None:
|
||||
"""Returns glibc version string, or None if not using glibc."""
|
||||
return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
|
||||
|
||||
|
||||
def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
|
||||
def _parse_glibc_version(version_str: str) -> tuple[int, int]:
|
||||
"""Parse glibc version.
|
||||
|
||||
We use a regexp instead of str.split because we want to discard any
|
||||
|
|
@ -208,19 +215,19 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
|
|||
in patched/forked versions of glibc (e.g. Linaro's version of glibc
|
||||
uses version strings like "2.20-2014.11"). See gh-3588.
|
||||
"""
|
||||
m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
|
||||
m = re.match(r'(?P<major>[0-9]+)\.(?P<minor>[0-9]+)', version_str)
|
||||
if not m:
|
||||
warnings.warn(
|
||||
"Expected glibc version with 2 components major.minor,"
|
||||
" got: %s" % version_str,
|
||||
'Expected glibc version with 2 components major.minor,'
|
||||
' got: %s' % version_str,
|
||||
RuntimeWarning,
|
||||
)
|
||||
return -1, -1
|
||||
return int(m.group("major")), int(m.group("minor"))
|
||||
return int(m.group('major')), int(m.group('minor'))
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def _get_glibc_version() -> Tuple[int, int]:
|
||||
@functools.lru_cache
|
||||
def _get_glibc_version() -> tuple[int, int]:
|
||||
version_str = _glibc_version_string()
|
||||
if version_str is None:
|
||||
return (-1, -1)
|
||||
|
|
@ -237,30 +244,30 @@ def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool:
|
|||
import _manylinux # noqa
|
||||
except ImportError:
|
||||
return True
|
||||
if hasattr(_manylinux, "manylinux_compatible"):
|
||||
if hasattr(_manylinux, 'manylinux_compatible'):
|
||||
result = _manylinux.manylinux_compatible(version[0], version[1], arch)
|
||||
if result is not None:
|
||||
return bool(result)
|
||||
return True
|
||||
if version == _GLibCVersion(2, 5):
|
||||
if hasattr(_manylinux, "manylinux1_compatible"):
|
||||
if hasattr(_manylinux, 'manylinux1_compatible'):
|
||||
return bool(_manylinux.manylinux1_compatible)
|
||||
if version == _GLibCVersion(2, 12):
|
||||
if hasattr(_manylinux, "manylinux2010_compatible"):
|
||||
if hasattr(_manylinux, 'manylinux2010_compatible'):
|
||||
return bool(_manylinux.manylinux2010_compatible)
|
||||
if version == _GLibCVersion(2, 17):
|
||||
if hasattr(_manylinux, "manylinux2014_compatible"):
|
||||
if hasattr(_manylinux, 'manylinux2014_compatible'):
|
||||
return bool(_manylinux.manylinux2014_compatible)
|
||||
return True
|
||||
|
||||
|
||||
_LEGACY_MANYLINUX_MAP = {
|
||||
# CentOS 7 w/ glibc 2.17 (PEP 599)
|
||||
(2, 17): "manylinux2014",
|
||||
(2, 17): 'manylinux2014',
|
||||
# CentOS 6 w/ glibc 2.12 (PEP 571)
|
||||
(2, 12): "manylinux2010",
|
||||
(2, 12): 'manylinux2010',
|
||||
# CentOS 5 w/ glibc 2.5 (PEP 513)
|
||||
(2, 5): "manylinux1",
|
||||
(2, 5): 'manylinux1',
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -269,7 +276,7 @@ def platform_tags(linux: str, arch: str) -> Iterator[str]:
|
|||
return
|
||||
# Oldest glibc to be supported regardless of architecture is (2, 17).
|
||||
too_old_glibc2 = _GLibCVersion(2, 16)
|
||||
if arch in {"x86_64", "i686"}:
|
||||
if arch in {'x86_64', 'i686'}:
|
||||
# On x86/i686 also oldest glibc to be supported is (2, 5).
|
||||
too_old_glibc2 = _GLibCVersion(2, 4)
|
||||
current_glibc = _GLibCVersion(*_get_glibc_version())
|
||||
|
|
@ -291,11 +298,11 @@ def platform_tags(linux: str, arch: str) -> Iterator[str]:
|
|||
min_minor = -1
|
||||
for glibc_minor in range(glibc_max.minor, min_minor, -1):
|
||||
glibc_version = _GLibCVersion(glibc_max.major, glibc_minor)
|
||||
tag = "manylinux_{}_{}".format(*glibc_version)
|
||||
tag = 'manylinux_{}_{}'.format(*glibc_version)
|
||||
if _is_compatible(tag, arch, glibc_version):
|
||||
yield linux.replace("linux", tag)
|
||||
yield linux.replace('linux', tag)
|
||||
# Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
|
||||
if glibc_version in _LEGACY_MANYLINUX_MAP:
|
||||
legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version]
|
||||
if _is_compatible(legacy_tag, arch, glibc_version):
|
||||
yield linux.replace("linux", legacy_tag)
|
||||
yield linux.replace('linux', legacy_tag)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
This module implements logic to detect if the currently running Python is
|
||||
linked against musl, and what musl version is used.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
|
|
@ -12,14 +13,18 @@ import re
|
|||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import IO, Iterator, NamedTuple, Optional, Tuple
|
||||
from typing import IO
|
||||
from typing import Iterator
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]:
|
||||
def _read_unpacked(f: IO[bytes], fmt: str) -> tuple[int, ...]:
|
||||
return struct.unpack(fmt, f.read(struct.calcsize(fmt)))
|
||||
|
||||
|
||||
def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]:
|
||||
def _parse_ld_musl_from_elf(f: IO[bytes]) -> str | None:
|
||||
"""Detect musl libc location by parsing the Python executable.
|
||||
|
||||
Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
|
||||
|
|
@ -27,20 +32,20 @@ def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]:
|
|||
"""
|
||||
f.seek(0)
|
||||
try:
|
||||
ident = _read_unpacked(f, "16B")
|
||||
ident = _read_unpacked(f, '16B')
|
||||
except struct.error:
|
||||
return None
|
||||
if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF.
|
||||
if ident[:4] != tuple(b'\x7fELF'): # Invalid magic, not ELF.
|
||||
return None
|
||||
f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version.
|
||||
f.seek(struct.calcsize('HHI'), 1) # Skip file type, machine, and version.
|
||||
|
||||
try:
|
||||
# e_fmt: Format for program header.
|
||||
# p_fmt: Format for section header.
|
||||
# p_idx: Indexes to find p_type, p_offset, and p_filesz.
|
||||
e_fmt, p_fmt, p_idx = {
|
||||
1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit.
|
||||
2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit.
|
||||
1: ('IIIIHHH', 'IIIIIIII', (0, 1, 4)), # 32-bit.
|
||||
2: ('QQQIHHH', 'IIQQQQQQ', (0, 2, 5)), # 64-bit.
|
||||
}[ident[4]]
|
||||
except KeyError:
|
||||
return None
|
||||
|
|
@ -61,8 +66,8 @@ def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]:
|
|||
if p_type != 3: # Not PT_INTERP.
|
||||
continue
|
||||
f.seek(p_offset)
|
||||
interpreter = os.fsdecode(f.read(p_filesz)).strip("\0")
|
||||
if "musl" not in interpreter:
|
||||
interpreter = os.fsdecode(f.read(p_filesz)).strip('\0')
|
||||
if 'musl' not in interpreter:
|
||||
return None
|
||||
return interpreter
|
||||
return None
|
||||
|
|
@ -73,18 +78,18 @@ class _MuslVersion(NamedTuple):
|
|||
minor: int
|
||||
|
||||
|
||||
def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
|
||||
def _parse_musl_version(output: str) -> _MuslVersion | None:
|
||||
lines = [n for n in (n.strip() for n in output.splitlines()) if n]
|
||||
if len(lines) < 2 or lines[0][:4] != "musl":
|
||||
if len(lines) < 2 or lines[0][:4] != 'musl':
|
||||
return None
|
||||
m = re.match(r"Version (\d+)\.(\d+)", lines[1])
|
||||
m = re.match(r'Version (\d+)\.(\d+)', lines[1])
|
||||
if not m:
|
||||
return None
|
||||
return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
|
||||
@functools.lru_cache
|
||||
def _get_musl_version(executable: str) -> _MuslVersion | None:
|
||||
"""Detect currently-running musl runtime version.
|
||||
|
||||
This is done by checking the specified executable's dynamic linking
|
||||
|
|
@ -97,13 +102,13 @@ def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
|
|||
"""
|
||||
with contextlib.ExitStack() as stack:
|
||||
try:
|
||||
f = stack.enter_context(open(executable, "rb"))
|
||||
f = stack.enter_context(open(executable, 'rb'))
|
||||
except OSError:
|
||||
return None
|
||||
ld = _parse_ld_musl_from_elf(f)
|
||||
if not ld:
|
||||
return None
|
||||
proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True)
|
||||
proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True)
|
||||
return _parse_musl_version(proc.stderr)
|
||||
|
||||
|
||||
|
|
@ -120,17 +125,17 @@ def platform_tags(arch: str) -> Iterator[str]:
|
|||
if sys_musl is None: # Python not dynamically linked against musl.
|
||||
return
|
||||
for minor in range(sys_musl.minor, -1, -1):
|
||||
yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
|
||||
yield f'musllinux_{sys_musl.major}_{minor}_{arch}'
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
import sysconfig
|
||||
|
||||
plat = sysconfig.get_platform()
|
||||
assert plat.startswith("linux-"), "not linux"
|
||||
assert plat.startswith('linux-'), 'not linux'
|
||||
|
||||
print("plat:", plat)
|
||||
print("musl:", _get_musl_version(sys.executable))
|
||||
print("tags:", end=" ")
|
||||
for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
|
||||
print(t, end="\n ")
|
||||
print('plat:', plat)
|
||||
print('musl:', _get_musl_version(sys.executable))
|
||||
print('tags:', end=' ')
|
||||
for t in platform_tags(re.sub(r'[.-]', '_', plat.split('-', 1)[-1])):
|
||||
print(t, end='\n ')
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
class InfinityType:
|
||||
def __repr__(self) -> str:
|
||||
return "Infinity"
|
||||
return 'Infinity'
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(repr(self))
|
||||
|
|
@ -25,7 +26,7 @@ class InfinityType:
|
|||
def __ge__(self, other: object) -> bool:
|
||||
return True
|
||||
|
||||
def __neg__(self: object) -> "NegativeInfinityType":
|
||||
def __neg__(self: object) -> NegativeInfinityType:
|
||||
return NegativeInfinity
|
||||
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ Infinity = InfinityType()
|
|||
|
||||
class NegativeInfinityType:
|
||||
def __repr__(self) -> str:
|
||||
return "-Infinity"
|
||||
return '-Infinity'
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(repr(self))
|
||||
|
|
|
|||
|
|
@ -1,33 +1,39 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
import operator
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from pip._vendor.pyparsing import ( # noqa: N817
|
||||
Forward,
|
||||
Group,
|
||||
Literal as L,
|
||||
ParseException,
|
||||
ParseResults,
|
||||
QuotedString,
|
||||
ZeroOrMore,
|
||||
stringEnd,
|
||||
stringStart,
|
||||
)
|
||||
from pip._vendor.pyparsing import Forward
|
||||
from pip._vendor.pyparsing import Group
|
||||
from pip._vendor.pyparsing import Literal as L
|
||||
from pip._vendor.pyparsing import ParseException
|
||||
from pip._vendor.pyparsing import ParseResults
|
||||
from pip._vendor.pyparsing import QuotedString
|
||||
from pip._vendor.pyparsing import stringEnd
|
||||
from pip._vendor.pyparsing import stringStart
|
||||
from pip._vendor.pyparsing import ZeroOrMore
|
||||
|
||||
from .specifiers import InvalidSpecifier, Specifier
|
||||
from .specifiers import InvalidSpecifier
|
||||
from .specifiers import Specifier
|
||||
|
||||
__all__ = [
|
||||
"InvalidMarker",
|
||||
"UndefinedComparison",
|
||||
"UndefinedEnvironmentName",
|
||||
"Marker",
|
||||
"default_environment",
|
||||
'InvalidMarker',
|
||||
'UndefinedComparison',
|
||||
'UndefinedEnvironmentName',
|
||||
'Marker',
|
||||
'default_environment',
|
||||
]
|
||||
|
||||
Operator = Callable[[str, str], bool]
|
||||
|
|
@ -82,54 +88,54 @@ class Op(Node):
|
|||
|
||||
|
||||
VARIABLE = (
|
||||
L("implementation_version")
|
||||
| L("platform_python_implementation")
|
||||
| L("implementation_name")
|
||||
| L("python_full_version")
|
||||
| L("platform_release")
|
||||
| L("platform_version")
|
||||
| L("platform_machine")
|
||||
| L("platform_system")
|
||||
| L("python_version")
|
||||
| L("sys_platform")
|
||||
| L("os_name")
|
||||
| L("os.name") # PEP-345
|
||||
| L("sys.platform") # PEP-345
|
||||
| L("platform.version") # PEP-345
|
||||
| L("platform.machine") # PEP-345
|
||||
| L("platform.python_implementation") # PEP-345
|
||||
| L("python_implementation") # undocumented setuptools legacy
|
||||
| L("extra") # PEP-508
|
||||
L('implementation_version') |
|
||||
L('platform_python_implementation') |
|
||||
L('implementation_name') |
|
||||
L('python_full_version') |
|
||||
L('platform_release') |
|
||||
L('platform_version') |
|
||||
L('platform_machine') |
|
||||
L('platform_system') |
|
||||
L('python_version') |
|
||||
L('sys_platform') |
|
||||
L('os_name') |
|
||||
L('os.name') | # PEP-345
|
||||
L('sys.platform') | # PEP-345
|
||||
L('platform.version') | # PEP-345
|
||||
L('platform.machine') | # PEP-345
|
||||
L('platform.python_implementation') | # PEP-345
|
||||
L('python_implementation') | # undocumented setuptools legacy
|
||||
L('extra') # PEP-508
|
||||
)
|
||||
ALIASES = {
|
||||
"os.name": "os_name",
|
||||
"sys.platform": "sys_platform",
|
||||
"platform.version": "platform_version",
|
||||
"platform.machine": "platform_machine",
|
||||
"platform.python_implementation": "platform_python_implementation",
|
||||
"python_implementation": "platform_python_implementation",
|
||||
'os.name': 'os_name',
|
||||
'sys.platform': 'sys_platform',
|
||||
'platform.version': 'platform_version',
|
||||
'platform.machine': 'platform_machine',
|
||||
'platform.python_implementation': 'platform_python_implementation',
|
||||
'python_implementation': 'platform_python_implementation',
|
||||
}
|
||||
VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
|
||||
|
||||
VERSION_CMP = (
|
||||
L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<")
|
||||
L('===') | L('==') | L('>=') | L('<=') | L('!=') | L('~=') | L('>') | L('<')
|
||||
)
|
||||
|
||||
MARKER_OP = VERSION_CMP | L("not in") | L("in")
|
||||
MARKER_OP = VERSION_CMP | L('not in') | L('in')
|
||||
MARKER_OP.setParseAction(lambda s, l, t: Op(t[0]))
|
||||
|
||||
MARKER_VALUE = QuotedString("'") | QuotedString('"')
|
||||
MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0]))
|
||||
|
||||
BOOLOP = L("and") | L("or")
|
||||
BOOLOP = L('and') | L('or')
|
||||
|
||||
MARKER_VAR = VARIABLE | MARKER_VALUE
|
||||
|
||||
MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR)
|
||||
MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0]))
|
||||
|
||||
LPAREN = L("(").suppress()
|
||||
RPAREN = L(")").suppress()
|
||||
LPAREN = L('(').suppress()
|
||||
RPAREN = L(')').suppress()
|
||||
|
||||
MARKER_EXPR = Forward()
|
||||
MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN)
|
||||
|
|
@ -138,7 +144,7 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR)
|
|||
MARKER = stringStart + MARKER_EXPR + stringEnd
|
||||
|
||||
|
||||
def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]:
|
||||
def _coerce_parse_result(results: ParseResults | list[Any]) -> list[Any]:
|
||||
if isinstance(results, ParseResults):
|
||||
return [_coerce_parse_result(i) for i in results]
|
||||
else:
|
||||
|
|
@ -146,7 +152,7 @@ def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]:
|
|||
|
||||
|
||||
def _format_marker(
|
||||
marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True
|
||||
marker: list[str] | tuple[Node, ...] | str, first: bool | None = True,
|
||||
) -> str:
|
||||
|
||||
assert isinstance(marker, (list, tuple, str))
|
||||
|
|
@ -156,47 +162,47 @@ def _format_marker(
|
|||
# the rest of this function so that we don't get extraneous () on the
|
||||
# outside.
|
||||
if (
|
||||
isinstance(marker, list)
|
||||
and len(marker) == 1
|
||||
and isinstance(marker[0], (list, tuple))
|
||||
isinstance(marker, list) and
|
||||
len(marker) == 1 and
|
||||
isinstance(marker[0], (list, tuple))
|
||||
):
|
||||
return _format_marker(marker[0])
|
||||
|
||||
if isinstance(marker, list):
|
||||
inner = (_format_marker(m, first=False) for m in marker)
|
||||
if first:
|
||||
return " ".join(inner)
|
||||
return ' '.join(inner)
|
||||
else:
|
||||
return "(" + " ".join(inner) + ")"
|
||||
return '(' + ' '.join(inner) + ')'
|
||||
elif isinstance(marker, tuple):
|
||||
return " ".join([m.serialize() for m in marker])
|
||||
return ' '.join([m.serialize() for m in marker])
|
||||
else:
|
||||
return marker
|
||||
|
||||
|
||||
_operators: Dict[str, Operator] = {
|
||||
"in": lambda lhs, rhs: lhs in rhs,
|
||||
"not in": lambda lhs, rhs: lhs not in rhs,
|
||||
"<": operator.lt,
|
||||
"<=": operator.le,
|
||||
"==": operator.eq,
|
||||
"!=": operator.ne,
|
||||
">=": operator.ge,
|
||||
">": operator.gt,
|
||||
_operators: dict[str, Operator] = {
|
||||
'in': lambda lhs, rhs: lhs in rhs,
|
||||
'not in': lambda lhs, rhs: lhs not in rhs,
|
||||
'<': operator.lt,
|
||||
'<=': operator.le,
|
||||
'==': operator.eq,
|
||||
'!=': operator.ne,
|
||||
'>=': operator.ge,
|
||||
'>': operator.gt,
|
||||
}
|
||||
|
||||
|
||||
def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
|
||||
try:
|
||||
spec = Specifier("".join([op.serialize(), rhs]))
|
||||
spec = Specifier(''.join([op.serialize(), rhs]))
|
||||
except InvalidSpecifier:
|
||||
pass
|
||||
else:
|
||||
return spec.contains(lhs)
|
||||
|
||||
oper: Optional[Operator] = _operators.get(op.serialize())
|
||||
oper: Operator | None = _operators.get(op.serialize())
|
||||
if oper is None:
|
||||
raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
|
||||
raise UndefinedComparison(f'Undefined {op!r} on {lhs!r} and {rhs!r}.')
|
||||
|
||||
return oper(lhs, rhs)
|
||||
|
||||
|
|
@ -208,19 +214,19 @@ class Undefined:
|
|||
_undefined = Undefined()
|
||||
|
||||
|
||||
def _get_env(environment: Dict[str, str], name: str) -> str:
|
||||
value: Union[str, Undefined] = environment.get(name, _undefined)
|
||||
def _get_env(environment: dict[str, str], name: str) -> str:
|
||||
value: str | Undefined = environment.get(name, _undefined)
|
||||
|
||||
if isinstance(value, Undefined):
|
||||
raise UndefinedEnvironmentName(
|
||||
f"{name!r} does not exist in evaluation environment."
|
||||
f'{name!r} does not exist in evaluation environment.',
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool:
|
||||
groups: List[List[bool]] = [[]]
|
||||
def _evaluate_markers(markers: list[Any], environment: dict[str, str]) -> bool:
|
||||
groups: list[list[bool]] = [[]]
|
||||
|
||||
for marker in markers:
|
||||
assert isinstance(marker, (list, tuple, str))
|
||||
|
|
@ -239,36 +245,36 @@ def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool:
|
|||
|
||||
groups[-1].append(_eval_op(lhs_value, op, rhs_value))
|
||||
else:
|
||||
assert marker in ["and", "or"]
|
||||
if marker == "or":
|
||||
assert marker in ['and', 'or']
|
||||
if marker == 'or':
|
||||
groups.append([])
|
||||
|
||||
return any(all(item) for item in groups)
|
||||
|
||||
|
||||
def format_full_version(info: "sys._version_info") -> str:
|
||||
version = "{0.major}.{0.minor}.{0.micro}".format(info)
|
||||
def format_full_version(info: sys._version_info) -> str:
|
||||
version = '{0.major}.{0.minor}.{0.micro}'.format(info)
|
||||
kind = info.releaselevel
|
||||
if kind != "final":
|
||||
if kind != 'final':
|
||||
version += kind[0] + str(info.serial)
|
||||
return version
|
||||
|
||||
|
||||
def default_environment() -> Dict[str, str]:
|
||||
def default_environment() -> dict[str, str]:
|
||||
iver = format_full_version(sys.implementation.version)
|
||||
implementation_name = sys.implementation.name
|
||||
return {
|
||||
"implementation_name": implementation_name,
|
||||
"implementation_version": iver,
|
||||
"os_name": os.name,
|
||||
"platform_machine": platform.machine(),
|
||||
"platform_release": platform.release(),
|
||||
"platform_system": platform.system(),
|
||||
"platform_version": platform.version(),
|
||||
"python_full_version": platform.python_version(),
|
||||
"platform_python_implementation": platform.python_implementation(),
|
||||
"python_version": ".".join(platform.python_version_tuple()[:2]),
|
||||
"sys_platform": sys.platform,
|
||||
'implementation_name': implementation_name,
|
||||
'implementation_version': iver,
|
||||
'os_name': os.name,
|
||||
'platform_machine': platform.machine(),
|
||||
'platform_release': platform.release(),
|
||||
'platform_system': platform.system(),
|
||||
'platform_version': platform.version(),
|
||||
'python_full_version': platform.python_version(),
|
||||
'platform_python_implementation': platform.python_implementation(),
|
||||
'python_version': '.'.join(platform.python_version_tuple()[:2]),
|
||||
'sys_platform': sys.platform,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -278,8 +284,8 @@ class Marker:
|
|||
self._markers = _coerce_parse_result(MARKER.parseString(marker))
|
||||
except ParseException as e:
|
||||
raise InvalidMarker(
|
||||
f"Invalid marker: {marker!r}, parse error at "
|
||||
f"{marker[e.loc : e.loc + 8]!r}"
|
||||
f'Invalid marker: {marker!r}, parse error at '
|
||||
f'{marker[e.loc : e.loc + 8]!r}',
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
|
|
@ -288,7 +294,7 @@ class Marker:
|
|||
def __repr__(self) -> str:
|
||||
return f"<Marker('{self}')>"
|
||||
|
||||
def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
|
||||
def evaluate(self, environment: dict[str, str] | None = None) -> bool:
|
||||
"""Evaluate a marker.
|
||||
|
||||
Return the boolean from evaluating the given marker against the
|
||||
|
|
|
|||
|
|
@ -1,27 +1,31 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import string
|
||||
import urllib.parse
|
||||
from typing import List, Optional as TOptional, Set
|
||||
from typing import List
|
||||
from typing import Optional as TOptional
|
||||
from typing import Set
|
||||
|
||||
from pip._vendor.pyparsing import ( # noqa
|
||||
Combine,
|
||||
Literal as L,
|
||||
Optional,
|
||||
ParseException,
|
||||
Regex,
|
||||
Word,
|
||||
ZeroOrMore,
|
||||
originalTextFor,
|
||||
stringEnd,
|
||||
stringStart,
|
||||
)
|
||||
from pip._vendor.pyparsing import Combine
|
||||
from pip._vendor.pyparsing import Literal as L
|
||||
from pip._vendor.pyparsing import Optional
|
||||
from pip._vendor.pyparsing import originalTextFor
|
||||
from pip._vendor.pyparsing import ParseException
|
||||
from pip._vendor.pyparsing import Regex
|
||||
from pip._vendor.pyparsing import stringEnd
|
||||
from pip._vendor.pyparsing import stringStart
|
||||
from pip._vendor.pyparsing import Word
|
||||
from pip._vendor.pyparsing import ZeroOrMore
|
||||
|
||||
from .markers import MARKER_EXPR, Marker
|
||||
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
|
||||
from .markers import Marker
|
||||
from .markers import MARKER_EXPR
|
||||
from .specifiers import LegacySpecifier
|
||||
from .specifiers import Specifier
|
||||
from .specifiers import SpecifierSet
|
||||
|
||||
|
||||
class InvalidRequirement(ValueError):
|
||||
|
|
@ -32,43 +36,43 @@ class InvalidRequirement(ValueError):
|
|||
|
||||
ALPHANUM = Word(string.ascii_letters + string.digits)
|
||||
|
||||
LBRACKET = L("[").suppress()
|
||||
RBRACKET = L("]").suppress()
|
||||
LPAREN = L("(").suppress()
|
||||
RPAREN = L(")").suppress()
|
||||
COMMA = L(",").suppress()
|
||||
SEMICOLON = L(";").suppress()
|
||||
AT = L("@").suppress()
|
||||
LBRACKET = L('[').suppress()
|
||||
RBRACKET = L(']').suppress()
|
||||
LPAREN = L('(').suppress()
|
||||
RPAREN = L(')').suppress()
|
||||
COMMA = L(',').suppress()
|
||||
SEMICOLON = L(';').suppress()
|
||||
AT = L('@').suppress()
|
||||
|
||||
PUNCTUATION = Word("-_.")
|
||||
PUNCTUATION = Word('-_.')
|
||||
IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM)
|
||||
IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END))
|
||||
|
||||
NAME = IDENTIFIER("name")
|
||||
NAME = IDENTIFIER('name')
|
||||
EXTRA = IDENTIFIER
|
||||
|
||||
URI = Regex(r"[^ ]+")("url")
|
||||
URI = Regex(r'[^ ]+')('url')
|
||||
URL = AT + URI
|
||||
|
||||
EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA)
|
||||
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras")
|
||||
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)('extras')
|
||||
|
||||
VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE)
|
||||
VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
|
||||
VERSION_MANY = Combine(
|
||||
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
|
||||
)("_raw_spec")
|
||||
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=',', adjacent=False,
|
||||
)('_raw_spec')
|
||||
_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)
|
||||
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
|
||||
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '')
|
||||
|
||||
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
|
||||
VERSION_SPEC = originalTextFor(_VERSION_SPEC)('specifier')
|
||||
VERSION_SPEC.setParseAction(lambda s, l, t: t[1])
|
||||
|
||||
MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
|
||||
MARKER_EXPR = originalTextFor(MARKER_EXPR())('marker')
|
||||
MARKER_EXPR.setParseAction(
|
||||
lambda s, l, t: Marker(s[t._original_start : t._original_end])
|
||||
lambda s, l, t: Marker(s[t._original_start: t._original_end]),
|
||||
)
|
||||
MARKER_SEPARATOR = SEMICOLON
|
||||
MARKER = MARKER_SEPARATOR + MARKER_EXPR
|
||||
|
|
@ -81,7 +85,7 @@ NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARK
|
|||
REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
|
||||
# pyparsing isn't thread safe during initialization, so we do it eagerly, see
|
||||
# issue #104
|
||||
REQUIREMENT.parseString("x[]")
|
||||
REQUIREMENT.parseString('x[]')
|
||||
|
||||
|
||||
class Requirement:
|
||||
|
|
@ -102,45 +106,45 @@ class Requirement:
|
|||
req = REQUIREMENT.parseString(requirement_string)
|
||||
except ParseException as e:
|
||||
raise InvalidRequirement(
|
||||
f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}'
|
||||
f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}',
|
||||
)
|
||||
|
||||
self.name: str = req.name
|
||||
if req.url:
|
||||
parsed_url = urllib.parse.urlparse(req.url)
|
||||
if parsed_url.scheme == "file":
|
||||
if parsed_url.scheme == 'file':
|
||||
if urllib.parse.urlunparse(parsed_url) != req.url:
|
||||
raise InvalidRequirement("Invalid URL given")
|
||||
raise InvalidRequirement('Invalid URL given')
|
||||
elif not (parsed_url.scheme and parsed_url.netloc) or (
|
||||
not parsed_url.scheme and not parsed_url.netloc
|
||||
):
|
||||
raise InvalidRequirement(f"Invalid URL: {req.url}")
|
||||
raise InvalidRequirement(f'Invalid URL: {req.url}')
|
||||
self.url: TOptional[str] = req.url
|
||||
else:
|
||||
self.url = None
|
||||
self.extras: Set[str] = set(req.extras.asList() if req.extras else [])
|
||||
self.extras: set[str] = set(req.extras.asList() if req.extras else [])
|
||||
self.specifier: SpecifierSet = SpecifierSet(req.specifier)
|
||||
self.marker: TOptional[Marker] = req.marker if req.marker else None
|
||||
|
||||
def __str__(self) -> str:
|
||||
parts: List[str] = [self.name]
|
||||
parts: list[str] = [self.name]
|
||||
|
||||
if self.extras:
|
||||
formatted_extras = ",".join(sorted(self.extras))
|
||||
parts.append(f"[{formatted_extras}]")
|
||||
formatted_extras = ','.join(sorted(self.extras))
|
||||
parts.append(f'[{formatted_extras}]')
|
||||
|
||||
if self.specifier:
|
||||
parts.append(str(self.specifier))
|
||||
|
||||
if self.url:
|
||||
parts.append(f"@ {self.url}")
|
||||
parts.append(f'@ {self.url}')
|
||||
if self.marker:
|
||||
parts.append(" ")
|
||||
parts.append(' ')
|
||||
|
||||
if self.marker:
|
||||
parts.append(f"; {self.marker}")
|
||||
parts.append(f'; {self.marker}')
|
||||
|
||||
return "".join(parts)
|
||||
return ''.join(parts)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Requirement('{self}')>"
|
||||
|
|
|
|||
|
|
@ -1,32 +1,33 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import functools
|
||||
import itertools
|
||||
import re
|
||||
import warnings
|
||||
from typing import (
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Pattern,
|
||||
Set,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Pattern
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
from typing import TypeVar
|
||||
from typing import Union
|
||||
|
||||
from .utils import canonicalize_version
|
||||
from .version import LegacyVersion, Version, parse
|
||||
from .version import LegacyVersion
|
||||
from .version import parse
|
||||
from .version import Version
|
||||
|
||||
ParsedVersion = Union[Version, LegacyVersion]
|
||||
UnparsedVersion = Union[Version, LegacyVersion, str]
|
||||
VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion)
|
||||
VersionTypeVar = TypeVar('VersionTypeVar', bound=UnparsedVersion)
|
||||
CallableOperator = Callable[[ParsedVersion, str], bool]
|
||||
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
|
|||
"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def prereleases(self) -> Optional[bool]:
|
||||
def prereleases(self) -> bool | None:
|
||||
"""
|
||||
Returns whether or not pre-releases as a whole are allowed by this
|
||||
specifier.
|
||||
|
|
@ -72,14 +73,14 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
|
|||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def contains(self, item: str, prereleases: Optional[bool] = None) -> bool:
|
||||
def contains(self, item: str, prereleases: bool | None = None) -> bool:
|
||||
"""
|
||||
Determines if the given item is contained within this specifier.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def filter(
|
||||
self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
|
||||
self, iterable: Iterable[VersionTypeVar], prereleases: bool | None = None,
|
||||
) -> Iterable[VersionTypeVar]:
|
||||
"""
|
||||
Takes an iterable of items and filters them so that only items which
|
||||
|
|
@ -89,17 +90,17 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
|
|||
|
||||
class _IndividualSpecifier(BaseSpecifier):
|
||||
|
||||
_operators: Dict[str, str] = {}
|
||||
_operators: dict[str, str] = {}
|
||||
_regex: Pattern[str]
|
||||
|
||||
def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
|
||||
def __init__(self, spec: str = '', prereleases: bool | None = None) -> None:
|
||||
match = self._regex.search(spec)
|
||||
if not match:
|
||||
raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
|
||||
|
||||
self._spec: Tuple[str, str] = (
|
||||
match.group("operator").strip(),
|
||||
match.group("version").strip(),
|
||||
self._spec: tuple[str, str] = (
|
||||
match.group('operator').strip(),
|
||||
match.group('version').strip(),
|
||||
)
|
||||
|
||||
# Store whether or not this Specifier should accept prereleases
|
||||
|
|
@ -107,18 +108,18 @@ class _IndividualSpecifier(BaseSpecifier):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
pre = (
|
||||
f", prereleases={self.prereleases!r}"
|
||||
f', prereleases={self.prereleases!r}'
|
||||
if self._prereleases is not None
|
||||
else ""
|
||||
else ''
|
||||
)
|
||||
|
||||
return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
|
||||
return f'<{self.__class__.__name__}({str(self)!r}{pre})>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "{}{}".format(*self._spec)
|
||||
return '{}{}'.format(*self._spec)
|
||||
|
||||
@property
|
||||
def _canonical_spec(self) -> Tuple[str, str]:
|
||||
def _canonical_spec(self) -> tuple[str, str]:
|
||||
return self._spec[0], canonicalize_version(self._spec[1])
|
||||
|
||||
def __hash__(self) -> int:
|
||||
|
|
@ -137,7 +138,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
|||
|
||||
def _get_operator(self, op: str) -> CallableOperator:
|
||||
operator_callable: CallableOperator = getattr(
|
||||
self, f"_compare_{self._operators[op]}"
|
||||
self, f'_compare_{self._operators[op]}',
|
||||
)
|
||||
return operator_callable
|
||||
|
||||
|
|
@ -155,7 +156,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
|||
return self._spec[1]
|
||||
|
||||
@property
|
||||
def prereleases(self) -> Optional[bool]:
|
||||
def prereleases(self) -> bool | None:
|
||||
return self._prereleases
|
||||
|
||||
@prereleases.setter
|
||||
|
|
@ -166,7 +167,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
|||
return self.contains(item)
|
||||
|
||||
def contains(
|
||||
self, item: UnparsedVersion, prereleases: Optional[bool] = None
|
||||
self, item: UnparsedVersion, prereleases: bool | None = None,
|
||||
) -> bool:
|
||||
|
||||
# Determine if prereleases are to be allowed or not.
|
||||
|
|
@ -189,13 +190,13 @@ class _IndividualSpecifier(BaseSpecifier):
|
|||
return operator_callable(normalized_item, self.version)
|
||||
|
||||
def filter(
|
||||
self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
|
||||
self, iterable: Iterable[VersionTypeVar], prereleases: bool | None = None,
|
||||
) -> Iterable[VersionTypeVar]:
|
||||
|
||||
yielded = False
|
||||
found_prereleases = []
|
||||
|
||||
kw = {"prereleases": prereleases if prereleases is not None else True}
|
||||
kw = {'prereleases': prereleases if prereleases is not None else True}
|
||||
|
||||
# Attempt to iterate over all the values in the iterable and if any of
|
||||
# them match, yield them.
|
||||
|
|
@ -238,23 +239,23 @@ class LegacySpecifier(_IndividualSpecifier):
|
|||
)
|
||||
"""
|
||||
|
||||
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
_regex = re.compile(r'^\s*' + _regex_str + r'\s*$', re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
_operators = {
|
||||
"==": "equal",
|
||||
"!=": "not_equal",
|
||||
"<=": "less_than_equal",
|
||||
">=": "greater_than_equal",
|
||||
"<": "less_than",
|
||||
">": "greater_than",
|
||||
'==': 'equal',
|
||||
'!=': 'not_equal',
|
||||
'<=': 'less_than_equal',
|
||||
'>=': 'greater_than_equal',
|
||||
'<': 'less_than',
|
||||
'>': 'greater_than',
|
||||
}
|
||||
|
||||
def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
|
||||
def __init__(self, spec: str = '', prereleases: bool | None = None) -> None:
|
||||
super().__init__(spec, prereleases)
|
||||
|
||||
warnings.warn(
|
||||
"Creating a LegacyVersion has been deprecated and will be "
|
||||
"removed in the next major release",
|
||||
'Creating a LegacyVersion has been deprecated and will be '
|
||||
'removed in the next major release',
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
|
|
@ -273,7 +274,7 @@ class LegacySpecifier(_IndividualSpecifier):
|
|||
return prospective <= self._coerce_version(spec)
|
||||
|
||||
def _compare_greater_than_equal(
|
||||
self, prospective: LegacyVersion, spec: str
|
||||
self, prospective: LegacyVersion, spec: str,
|
||||
) -> bool:
|
||||
return prospective >= self._coerce_version(spec)
|
||||
|
||||
|
|
@ -285,10 +286,10 @@ class LegacySpecifier(_IndividualSpecifier):
|
|||
|
||||
|
||||
def _require_version_compare(
|
||||
fn: Callable[["Specifier", ParsedVersion, str], bool]
|
||||
) -> Callable[["Specifier", ParsedVersion, str], bool]:
|
||||
fn: Callable[[Specifier, ParsedVersion, str], bool],
|
||||
) -> Callable[[Specifier, ParsedVersion, str], bool]:
|
||||
@functools.wraps(fn)
|
||||
def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool:
|
||||
def wrapped(self: Specifier, prospective: ParsedVersion, spec: str) -> bool:
|
||||
if not isinstance(prospective, Version):
|
||||
return False
|
||||
return fn(self, prospective, spec)
|
||||
|
|
@ -391,17 +392,17 @@ class Specifier(_IndividualSpecifier):
|
|||
)
|
||||
"""
|
||||
|
||||
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
_regex = re.compile(r'^\s*' + _regex_str + r'\s*$', re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
_operators = {
|
||||
"~=": "compatible",
|
||||
"==": "equal",
|
||||
"!=": "not_equal",
|
||||
"<=": "less_than_equal",
|
||||
">=": "greater_than_equal",
|
||||
"<": "less_than",
|
||||
">": "greater_than",
|
||||
"===": "arbitrary",
|
||||
'~=': 'compatible',
|
||||
'==': 'equal',
|
||||
'!=': 'not_equal',
|
||||
'<=': 'less_than_equal',
|
||||
'>=': 'greater_than_equal',
|
||||
'<': 'less_than',
|
||||
'>': 'greater_than',
|
||||
'===': 'arbitrary',
|
||||
}
|
||||
|
||||
@_require_version_compare
|
||||
|
|
@ -415,22 +416,22 @@ class Specifier(_IndividualSpecifier):
|
|||
|
||||
# We want everything but the last item in the version, but we want to
|
||||
# ignore suffix segments.
|
||||
prefix = ".".join(
|
||||
list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
|
||||
prefix = '.'.join(
|
||||
list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1],
|
||||
)
|
||||
|
||||
# Add the prefix notation to the end of our string
|
||||
prefix += ".*"
|
||||
prefix += '.*'
|
||||
|
||||
return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
|
||||
prospective, prefix
|
||||
return self._get_operator('>=')(prospective, spec) and self._get_operator('==')(
|
||||
prospective, prefix,
|
||||
)
|
||||
|
||||
@_require_version_compare
|
||||
def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool:
|
||||
|
||||
# We need special logic to handle prefix matching
|
||||
if spec.endswith(".*"):
|
||||
if spec.endswith('.*'):
|
||||
# In the case of prefix matching we want to ignore local segment.
|
||||
prospective = Version(prospective.public)
|
||||
# Split the spec out by dots, and pretend that there is an implicit
|
||||
|
|
@ -450,7 +451,7 @@ class Specifier(_IndividualSpecifier):
|
|||
# Pad out our two sides with zeros so that they both equal the same
|
||||
# length.
|
||||
padded_spec, padded_prospective = _pad_version(
|
||||
split_spec, shortened_prospective
|
||||
split_spec, shortened_prospective,
|
||||
)
|
||||
|
||||
return padded_prospective == padded_spec
|
||||
|
|
@ -480,7 +481,7 @@ class Specifier(_IndividualSpecifier):
|
|||
|
||||
@_require_version_compare
|
||||
def _compare_greater_than_equal(
|
||||
self, prospective: ParsedVersion, spec: str
|
||||
self, prospective: ParsedVersion, spec: str,
|
||||
) -> bool:
|
||||
|
||||
# NB: Local version identifiers are NOT permitted in the version
|
||||
|
|
@ -561,10 +562,10 @@ class Specifier(_IndividualSpecifier):
|
|||
# operators, and if they are if they are including an explicit
|
||||
# prerelease.
|
||||
operator, version = self._spec
|
||||
if operator in ["==", ">=", "<=", "~=", "==="]:
|
||||
if operator in ['==', '>=', '<=', '~=', '===']:
|
||||
# The == specifier can include a trailing .*, if it does we
|
||||
# want to remove before parsing.
|
||||
if operator == "==" and version.endswith(".*"):
|
||||
if operator == '==' and version.endswith('.*'):
|
||||
version = version[:-2]
|
||||
|
||||
# Parse the version, and if it is a pre-release than this
|
||||
|
|
@ -579,12 +580,12 @@ class Specifier(_IndividualSpecifier):
|
|||
self._prereleases = value
|
||||
|
||||
|
||||
_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
|
||||
_prefix_regex = re.compile(r'^([0-9]+)((?:a|b|c|rc)[0-9]+)$')
|
||||
|
||||
|
||||
def _version_split(version: str) -> List[str]:
|
||||
result: List[str] = []
|
||||
for item in version.split("."):
|
||||
def _version_split(version: str) -> list[str]:
|
||||
result: list[str] = []
|
||||
for item in version.split('.'):
|
||||
match = _prefix_regex.search(item)
|
||||
if match:
|
||||
result.extend(match.groups())
|
||||
|
|
@ -595,11 +596,11 @@ def _version_split(version: str) -> List[str]:
|
|||
|
||||
def _is_not_suffix(segment: str) -> bool:
|
||||
return not any(
|
||||
segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")
|
||||
segment.startswith(prefix) for prefix in ('dev', 'a', 'b', 'rc', 'post')
|
||||
)
|
||||
|
||||
|
||||
def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]:
|
||||
def _pad_version(left: list[str], right: list[str]) -> tuple[list[str], list[str]]:
|
||||
left_split, right_split = [], []
|
||||
|
||||
# Get the release segment of our versions
|
||||
|
|
@ -607,28 +608,28 @@ def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str
|
|||
right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
|
||||
|
||||
# Get the rest of our versions
|
||||
left_split.append(left[len(left_split[0]) :])
|
||||
right_split.append(right[len(right_split[0]) :])
|
||||
left_split.append(left[len(left_split[0]):])
|
||||
right_split.append(right[len(right_split[0]):])
|
||||
|
||||
# Insert our padding
|
||||
left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
|
||||
right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
|
||||
left_split.insert(1, ['0'] * max(0, len(right_split[0]) - len(left_split[0])))
|
||||
right_split.insert(1, ['0'] * max(0, len(left_split[0]) - len(right_split[0])))
|
||||
|
||||
return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
|
||||
|
||||
|
||||
class SpecifierSet(BaseSpecifier):
|
||||
def __init__(
|
||||
self, specifiers: str = "", prereleases: Optional[bool] = None
|
||||
self, specifiers: str = '', prereleases: bool | None = None,
|
||||
) -> None:
|
||||
|
||||
# Split on , to break each individual specifier into it's own item, and
|
||||
# strip each item to remove leading/trailing whitespace.
|
||||
split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
|
||||
split_specifiers = [s.strip() for s in specifiers.split(',') if s.strip()]
|
||||
|
||||
# Parsed each individual specifier, attempting first to make it a
|
||||
# Specifier and falling back to a LegacySpecifier.
|
||||
parsed: Set[_IndividualSpecifier] = set()
|
||||
parsed: set[_IndividualSpecifier] = set()
|
||||
for specifier in split_specifiers:
|
||||
try:
|
||||
parsed.add(Specifier(specifier))
|
||||
|
|
@ -644,20 +645,20 @@ class SpecifierSet(BaseSpecifier):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
pre = (
|
||||
f", prereleases={self.prereleases!r}"
|
||||
f', prereleases={self.prereleases!r}'
|
||||
if self._prereleases is not None
|
||||
else ""
|
||||
else ''
|
||||
)
|
||||
|
||||
return f"<SpecifierSet({str(self)!r}{pre})>"
|
||||
return f'<SpecifierSet({str(self)!r}{pre})>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return ",".join(sorted(str(s) for s in self._specs))
|
||||
return ','.join(sorted(str(s) for s in self._specs))
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self._specs)
|
||||
|
||||
def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
|
||||
def __and__(self, other: SpecifierSet | str) -> SpecifierSet:
|
||||
if isinstance(other, str):
|
||||
other = SpecifierSet(other)
|
||||
elif not isinstance(other, SpecifierSet):
|
||||
|
|
@ -674,8 +675,8 @@ class SpecifierSet(BaseSpecifier):
|
|||
specifier._prereleases = self._prereleases
|
||||
else:
|
||||
raise ValueError(
|
||||
"Cannot combine SpecifierSets with True and False prerelease "
|
||||
"overrides."
|
||||
'Cannot combine SpecifierSets with True and False prerelease '
|
||||
'overrides.',
|
||||
)
|
||||
|
||||
return specifier
|
||||
|
|
@ -695,7 +696,7 @@ class SpecifierSet(BaseSpecifier):
|
|||
return iter(self._specs)
|
||||
|
||||
@property
|
||||
def prereleases(self) -> Optional[bool]:
|
||||
def prereleases(self) -> bool | None:
|
||||
|
||||
# If we have been given an explicit prerelease modifier, then we'll
|
||||
# pass that through here.
|
||||
|
|
@ -720,7 +721,7 @@ class SpecifierSet(BaseSpecifier):
|
|||
return self.contains(item)
|
||||
|
||||
def contains(
|
||||
self, item: UnparsedVersion, prereleases: Optional[bool] = None
|
||||
self, item: UnparsedVersion, prereleases: bool | None = None,
|
||||
) -> bool:
|
||||
|
||||
# Ensure that our item is a Version or LegacyVersion instance.
|
||||
|
|
@ -749,7 +750,7 @@ class SpecifierSet(BaseSpecifier):
|
|||
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
|
||||
|
||||
def filter(
|
||||
self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
|
||||
self, iterable: Iterable[VersionTypeVar], prereleases: bool | None = None,
|
||||
) -> Iterable[VersionTypeVar]:
|
||||
|
||||
# Determine if we're forcing a prerelease or not, if we're not forcing
|
||||
|
|
@ -769,11 +770,11 @@ class SpecifierSet(BaseSpecifier):
|
|||
# which will filter out any pre-releases, unless there are no final
|
||||
# releases, and which will filter out LegacyVersion in general.
|
||||
else:
|
||||
filtered: List[VersionTypeVar] = []
|
||||
found_prereleases: List[VersionTypeVar] = []
|
||||
filtered: list[VersionTypeVar] = []
|
||||
found_prereleases: list[VersionTypeVar] = []
|
||||
|
||||
item: UnparsedVersion
|
||||
parsed_version: Union[Version, LegacyVersion]
|
||||
parsed_version: Version | LegacyVersion
|
||||
|
||||
for item in iterable:
|
||||
# Ensure that we some kind of Version class for this item.
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import platform
|
||||
import sys
|
||||
import sysconfig
|
||||
from importlib.machinery import EXTENSION_SUFFIXES
|
||||
from typing import (
|
||||
Dict,
|
||||
FrozenSet,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from typing import cast
|
||||
from typing import Dict
|
||||
from typing import FrozenSet
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from . import _manylinux, _musllinux
|
||||
from . import _manylinux
|
||||
from . import _musllinux
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PythonVersion = Sequence[int]
|
||||
MacVersion = Tuple[int, int]
|
||||
|
||||
INTERPRETER_SHORT_NAMES: Dict[str, str] = {
|
||||
"python": "py", # Generic.
|
||||
"cpython": "cp",
|
||||
"pypy": "pp",
|
||||
"ironpython": "ip",
|
||||
"jython": "jy",
|
||||
INTERPRETER_SHORT_NAMES: dict[str, str] = {
|
||||
'python': 'py', # Generic.
|
||||
'cpython': 'cp',
|
||||
'pypy': 'pp',
|
||||
'ironpython': 'ip',
|
||||
'jython': 'jy',
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ class Tag:
|
|||
is also supported.
|
||||
"""
|
||||
|
||||
__slots__ = ["_interpreter", "_abi", "_platform", "_hash"]
|
||||
__slots__ = ['_interpreter', '_abi', '_platform', '_hash']
|
||||
|
||||
def __init__(self, interpreter: str, abi: str, platform: str) -> None:
|
||||
self._interpreter = interpreter.lower()
|
||||
|
|
@ -77,23 +77,23 @@ class Tag:
|
|||
return NotImplemented
|
||||
|
||||
return (
|
||||
(self._hash == other._hash) # Short-circuit ASAP for perf reasons.
|
||||
and (self._platform == other._platform)
|
||||
and (self._abi == other._abi)
|
||||
and (self._interpreter == other._interpreter)
|
||||
(self._hash == other._hash) and # Short-circuit ASAP for perf reasons.
|
||||
(self._platform == other._platform) and
|
||||
(self._abi == other._abi) and
|
||||
(self._interpreter == other._interpreter)
|
||||
)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return self._hash
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self._interpreter}-{self._abi}-{self._platform}"
|
||||
return f'{self._interpreter}-{self._abi}-{self._platform}'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self} @ {id(self)}>"
|
||||
return f'<{self} @ {id(self)}>'
|
||||
|
||||
|
||||
def parse_tag(tag: str) -> FrozenSet[Tag]:
|
||||
def parse_tag(tag: str) -> frozenset[Tag]:
|
||||
"""
|
||||
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
|
||||
|
||||
|
|
@ -101,25 +101,25 @@ def parse_tag(tag: str) -> FrozenSet[Tag]:
|
|||
compressed tag set.
|
||||
"""
|
||||
tags = set()
|
||||
interpreters, abis, platforms = tag.split("-")
|
||||
for interpreter in interpreters.split("."):
|
||||
for abi in abis.split("."):
|
||||
for platform_ in platforms.split("."):
|
||||
interpreters, abis, platforms = tag.split('-')
|
||||
for interpreter in interpreters.split('.'):
|
||||
for abi in abis.split('.'):
|
||||
for platform_ in platforms.split('.'):
|
||||
tags.add(Tag(interpreter, abi, platform_))
|
||||
return frozenset(tags)
|
||||
|
||||
|
||||
def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]:
|
||||
def _get_config_var(name: str, warn: bool = False) -> int | str | None:
|
||||
value = sysconfig.get_config_var(name)
|
||||
if value is None and warn:
|
||||
logger.debug(
|
||||
"Config variable '%s' is unset, Python ABI tag may be incorrect", name
|
||||
"Config variable '%s' is unset, Python ABI tag may be incorrect", name,
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def _normalize_string(string: str) -> str:
|
||||
return string.replace(".", "_").replace("-", "_")
|
||||
return string.replace('.', '_').replace('-', '_')
|
||||
|
||||
|
||||
def _abi3_applies(python_version: PythonVersion) -> bool:
|
||||
|
|
@ -131,46 +131,46 @@ def _abi3_applies(python_version: PythonVersion) -> bool:
|
|||
return len(python_version) > 1 and tuple(python_version) >= (3, 2)
|
||||
|
||||
|
||||
def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
|
||||
def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]:
|
||||
py_version = tuple(py_version) # To allow for version comparison.
|
||||
abis = []
|
||||
version = _version_nodot(py_version[:2])
|
||||
debug = pymalloc = ucs4 = ""
|
||||
with_debug = _get_config_var("Py_DEBUG", warn)
|
||||
has_refcount = hasattr(sys, "gettotalrefcount")
|
||||
debug = pymalloc = ucs4 = ''
|
||||
with_debug = _get_config_var('Py_DEBUG', warn)
|
||||
has_refcount = hasattr(sys, 'gettotalrefcount')
|
||||
# Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
|
||||
# extension modules is the best option.
|
||||
# https://github.com/pypa/pip/issues/3383#issuecomment-173267692
|
||||
has_ext = "_d.pyd" in EXTENSION_SUFFIXES
|
||||
has_ext = '_d.pyd' in EXTENSION_SUFFIXES
|
||||
if with_debug or (with_debug is None and (has_refcount or has_ext)):
|
||||
debug = "d"
|
||||
debug = 'd'
|
||||
if py_version < (3, 8):
|
||||
with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
|
||||
with_pymalloc = _get_config_var('WITH_PYMALLOC', warn)
|
||||
if with_pymalloc or with_pymalloc is None:
|
||||
pymalloc = "m"
|
||||
pymalloc = 'm'
|
||||
if py_version < (3, 3):
|
||||
unicode_size = _get_config_var("Py_UNICODE_SIZE", warn)
|
||||
unicode_size = _get_config_var('Py_UNICODE_SIZE', warn)
|
||||
if unicode_size == 4 or (
|
||||
unicode_size is None and sys.maxunicode == 0x10FFFF
|
||||
):
|
||||
ucs4 = "u"
|
||||
ucs4 = 'u'
|
||||
elif debug:
|
||||
# Debug builds can also load "normal" extension modules.
|
||||
# We can also assume no UCS-4 or pymalloc requirement.
|
||||
abis.append(f"cp{version}")
|
||||
abis.append(f'cp{version}')
|
||||
abis.insert(
|
||||
0,
|
||||
"cp{version}{debug}{pymalloc}{ucs4}".format(
|
||||
version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4
|
||||
'cp{version}{debug}{pymalloc}{ucs4}'.format(
|
||||
version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4,
|
||||
),
|
||||
)
|
||||
return abis
|
||||
|
||||
|
||||
def cpython_tags(
|
||||
python_version: Optional[PythonVersion] = None,
|
||||
abis: Optional[Iterable[str]] = None,
|
||||
platforms: Optional[Iterable[str]] = None,
|
||||
python_version: PythonVersion | None = None,
|
||||
abis: Iterable[str] | None = None,
|
||||
platforms: Iterable[str] | None = None,
|
||||
*,
|
||||
warn: bool = False,
|
||||
) -> Iterator[Tag]:
|
||||
|
|
@ -192,7 +192,7 @@ def cpython_tags(
|
|||
if not python_version:
|
||||
python_version = sys.version_info[:2]
|
||||
|
||||
interpreter = f"cp{_version_nodot(python_version[:2])}"
|
||||
interpreter = f'cp{_version_nodot(python_version[:2])}'
|
||||
|
||||
if abis is None:
|
||||
if len(python_version) > 1:
|
||||
|
|
@ -201,7 +201,7 @@ def cpython_tags(
|
|||
abis = []
|
||||
abis = list(abis)
|
||||
# 'abi3' and 'none' are explicitly handled later.
|
||||
for explicit_abi in ("abi3", "none"):
|
||||
for explicit_abi in ('abi3', 'none'):
|
||||
try:
|
||||
abis.remove(explicit_abi)
|
||||
except ValueError:
|
||||
|
|
@ -212,28 +212,28 @@ def cpython_tags(
|
|||
for platform_ in platforms:
|
||||
yield Tag(interpreter, abi, platform_)
|
||||
if _abi3_applies(python_version):
|
||||
yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
|
||||
yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
|
||||
yield from (Tag(interpreter, 'abi3', platform_) for platform_ in platforms)
|
||||
yield from (Tag(interpreter, 'none', platform_) for platform_ in platforms)
|
||||
|
||||
if _abi3_applies(python_version):
|
||||
for minor_version in range(python_version[1] - 1, 1, -1):
|
||||
for platform_ in platforms:
|
||||
interpreter = "cp{version}".format(
|
||||
version=_version_nodot((python_version[0], minor_version))
|
||||
interpreter = 'cp{version}'.format(
|
||||
version=_version_nodot((python_version[0], minor_version)),
|
||||
)
|
||||
yield Tag(interpreter, "abi3", platform_)
|
||||
yield Tag(interpreter, 'abi3', platform_)
|
||||
|
||||
|
||||
def _generic_abi() -> Iterator[str]:
|
||||
abi = sysconfig.get_config_var("SOABI")
|
||||
abi = sysconfig.get_config_var('SOABI')
|
||||
if abi:
|
||||
yield _normalize_string(abi)
|
||||
|
||||
|
||||
def generic_tags(
|
||||
interpreter: Optional[str] = None,
|
||||
abis: Optional[Iterable[str]] = None,
|
||||
platforms: Optional[Iterable[str]] = None,
|
||||
interpreter: str | None = None,
|
||||
abis: Iterable[str] | None = None,
|
||||
platforms: Iterable[str] | None = None,
|
||||
*,
|
||||
warn: bool = False,
|
||||
) -> Iterator[Tag]:
|
||||
|
|
@ -248,13 +248,13 @@ def generic_tags(
|
|||
if not interpreter:
|
||||
interp_name = interpreter_name()
|
||||
interp_version = interpreter_version(warn=warn)
|
||||
interpreter = "".join([interp_name, interp_version])
|
||||
interpreter = ''.join([interp_name, interp_version])
|
||||
if abis is None:
|
||||
abis = _generic_abi()
|
||||
platforms = list(platforms or platform_tags())
|
||||
abis = list(abis)
|
||||
if "none" not in abis:
|
||||
abis.append("none")
|
||||
if 'none' not in abis:
|
||||
abis.append('none')
|
||||
for abi in abis:
|
||||
for platform_ in platforms:
|
||||
yield Tag(interpreter, abi, platform_)
|
||||
|
|
@ -268,17 +268,17 @@ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
|
|||
all previous versions of that major version.
|
||||
"""
|
||||
if len(py_version) > 1:
|
||||
yield f"py{_version_nodot(py_version[:2])}"
|
||||
yield f"py{py_version[0]}"
|
||||
yield f'py{_version_nodot(py_version[:2])}'
|
||||
yield f'py{py_version[0]}'
|
||||
if len(py_version) > 1:
|
||||
for minor in range(py_version[1] - 1, -1, -1):
|
||||
yield f"py{_version_nodot((py_version[0], minor))}"
|
||||
yield f'py{_version_nodot((py_version[0], minor))}'
|
||||
|
||||
|
||||
def compatible_tags(
|
||||
python_version: Optional[PythonVersion] = None,
|
||||
interpreter: Optional[str] = None,
|
||||
platforms: Optional[Iterable[str]] = None,
|
||||
python_version: PythonVersion | None = None,
|
||||
interpreter: str | None = None,
|
||||
platforms: Iterable[str] | None = None,
|
||||
) -> Iterator[Tag]:
|
||||
"""
|
||||
Yields the sequence of tags that are compatible with a specific version of Python.
|
||||
|
|
@ -293,57 +293,57 @@ def compatible_tags(
|
|||
platforms = list(platforms or platform_tags())
|
||||
for version in _py_interpreter_range(python_version):
|
||||
for platform_ in platforms:
|
||||
yield Tag(version, "none", platform_)
|
||||
yield Tag(version, 'none', platform_)
|
||||
if interpreter:
|
||||
yield Tag(interpreter, "none", "any")
|
||||
yield Tag(interpreter, 'none', 'any')
|
||||
for version in _py_interpreter_range(python_version):
|
||||
yield Tag(version, "none", "any")
|
||||
yield Tag(version, 'none', 'any')
|
||||
|
||||
|
||||
def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
|
||||
if not is_32bit:
|
||||
return arch
|
||||
|
||||
if arch.startswith("ppc"):
|
||||
return "ppc"
|
||||
if arch.startswith('ppc'):
|
||||
return 'ppc'
|
||||
|
||||
return "i386"
|
||||
return 'i386'
|
||||
|
||||
|
||||
def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
|
||||
def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]:
|
||||
formats = [cpu_arch]
|
||||
if cpu_arch == "x86_64":
|
||||
if cpu_arch == 'x86_64':
|
||||
if version < (10, 4):
|
||||
return []
|
||||
formats.extend(["intel", "fat64", "fat32"])
|
||||
formats.extend(['intel', 'fat64', 'fat32'])
|
||||
|
||||
elif cpu_arch == "i386":
|
||||
elif cpu_arch == 'i386':
|
||||
if version < (10, 4):
|
||||
return []
|
||||
formats.extend(["intel", "fat32", "fat"])
|
||||
formats.extend(['intel', 'fat32', 'fat'])
|
||||
|
||||
elif cpu_arch == "ppc64":
|
||||
elif cpu_arch == 'ppc64':
|
||||
# TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
|
||||
if version > (10, 5) or version < (10, 4):
|
||||
return []
|
||||
formats.append("fat64")
|
||||
formats.append('fat64')
|
||||
|
||||
elif cpu_arch == "ppc":
|
||||
elif cpu_arch == 'ppc':
|
||||
if version > (10, 6):
|
||||
return []
|
||||
formats.extend(["fat32", "fat"])
|
||||
formats.extend(['fat32', 'fat'])
|
||||
|
||||
if cpu_arch in {"arm64", "x86_64"}:
|
||||
formats.append("universal2")
|
||||
if cpu_arch in {'arm64', 'x86_64'}:
|
||||
formats.append('universal2')
|
||||
|
||||
if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}:
|
||||
formats.append("universal")
|
||||
if cpu_arch in {'x86_64', 'i386', 'ppc64', 'ppc', 'intel'}:
|
||||
formats.append('universal')
|
||||
|
||||
return formats
|
||||
|
||||
|
||||
def mac_platforms(
|
||||
version: Optional[MacVersion] = None, arch: Optional[str] = None
|
||||
version: MacVersion | None = None, arch: str | None = None,
|
||||
) -> Iterator[str]:
|
||||
"""
|
||||
Yields the platform tags for a macOS system.
|
||||
|
|
@ -355,7 +355,7 @@ def mac_platforms(
|
|||
"""
|
||||
version_str, _, cpu_arch = platform.mac_ver()
|
||||
if version is None:
|
||||
version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
|
||||
version = cast('MacVersion', tuple(map(int, version_str.split('.')[:2])))
|
||||
else:
|
||||
version = version
|
||||
if arch is None:
|
||||
|
|
@ -370,8 +370,8 @@ def mac_platforms(
|
|||
compat_version = 10, minor_version
|
||||
binary_formats = _mac_binary_formats(compat_version, arch)
|
||||
for binary_format in binary_formats:
|
||||
yield "macosx_{major}_{minor}_{binary_format}".format(
|
||||
major=10, minor=minor_version, binary_format=binary_format
|
||||
yield 'macosx_{major}_{minor}_{binary_format}'.format(
|
||||
major=10, minor=minor_version, binary_format=binary_format,
|
||||
)
|
||||
|
||||
if version >= (11, 0):
|
||||
|
|
@ -381,8 +381,8 @@ def mac_platforms(
|
|||
compat_version = major_version, 0
|
||||
binary_formats = _mac_binary_formats(compat_version, arch)
|
||||
for binary_format in binary_formats:
|
||||
yield "macosx_{major}_{minor}_{binary_format}".format(
|
||||
major=major_version, minor=0, binary_format=binary_format
|
||||
yield 'macosx_{major}_{minor}_{binary_format}'.format(
|
||||
major=major_version, minor=0, binary_format=binary_format,
|
||||
)
|
||||
|
||||
if version >= (11, 0):
|
||||
|
|
@ -393,12 +393,12 @@ def mac_platforms(
|
|||
# However, the "universal2" binary format can have a
|
||||
# macOS version earlier than 11.0 when the x86_64 part of the binary supports
|
||||
# that version of macOS.
|
||||
if arch == "x86_64":
|
||||
if arch == 'x86_64':
|
||||
for minor_version in range(16, 3, -1):
|
||||
compat_version = 10, minor_version
|
||||
binary_formats = _mac_binary_formats(compat_version, arch)
|
||||
for binary_format in binary_formats:
|
||||
yield "macosx_{major}_{minor}_{binary_format}".format(
|
||||
yield 'macosx_{major}_{minor}_{binary_format}'.format(
|
||||
major=compat_version[0],
|
||||
minor=compat_version[1],
|
||||
binary_format=binary_format,
|
||||
|
|
@ -406,8 +406,8 @@ def mac_platforms(
|
|||
else:
|
||||
for minor_version in range(16, 3, -1):
|
||||
compat_version = 10, minor_version
|
||||
binary_format = "universal2"
|
||||
yield "macosx_{major}_{minor}_{binary_format}".format(
|
||||
binary_format = 'universal2'
|
||||
yield 'macosx_{major}_{minor}_{binary_format}'.format(
|
||||
major=compat_version[0],
|
||||
minor=compat_version[1],
|
||||
binary_format=binary_format,
|
||||
|
|
@ -417,11 +417,11 @@ def mac_platforms(
|
|||
def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
|
||||
linux = _normalize_string(sysconfig.get_platform())
|
||||
if is_32bit:
|
||||
if linux == "linux_x86_64":
|
||||
linux = "linux_i686"
|
||||
elif linux == "linux_aarch64":
|
||||
linux = "linux_armv7l"
|
||||
_, arch = linux.split("_", 1)
|
||||
if linux == 'linux_x86_64':
|
||||
linux = 'linux_i686'
|
||||
elif linux == 'linux_aarch64':
|
||||
linux = 'linux_armv7l'
|
||||
_, arch = linux.split('_', 1)
|
||||
yield from _manylinux.platform_tags(linux, arch)
|
||||
yield from _musllinux.platform_tags(arch)
|
||||
yield linux
|
||||
|
|
@ -435,9 +435,9 @@ def platform_tags() -> Iterator[str]:
|
|||
"""
|
||||
Provides the platform tags for this installation.
|
||||
"""
|
||||
if platform.system() == "Darwin":
|
||||
if platform.system() == 'Darwin':
|
||||
return mac_platforms()
|
||||
elif platform.system() == "Linux":
|
||||
elif platform.system() == 'Linux':
|
||||
return _linux_platforms()
|
||||
else:
|
||||
return _generic_platforms()
|
||||
|
|
@ -455,7 +455,7 @@ def interpreter_version(*, warn: bool = False) -> str:
|
|||
"""
|
||||
Returns the version of the running interpreter.
|
||||
"""
|
||||
version = _get_config_var("py_version_nodot", warn=warn)
|
||||
version = _get_config_var('py_version_nodot', warn=warn)
|
||||
if version:
|
||||
version = str(version)
|
||||
else:
|
||||
|
|
@ -464,7 +464,7 @@ def interpreter_version(*, warn: bool = False) -> str:
|
|||
|
||||
|
||||
def _version_nodot(version: PythonVersion) -> str:
|
||||
return "".join(map(str, version))
|
||||
return ''.join(map(str, version))
|
||||
|
||||
|
||||
def sys_tags(*, warn: bool = False) -> Iterator[Tag]:
|
||||
|
|
@ -476,12 +476,12 @@ def sys_tags(*, warn: bool = False) -> Iterator[Tag]:
|
|||
"""
|
||||
|
||||
interp_name = interpreter_name()
|
||||
if interp_name == "cp":
|
||||
if interp_name == 'cp':
|
||||
yield from cpython_tags(warn=warn)
|
||||
else:
|
||||
yield from generic_tags()
|
||||
|
||||
if interp_name == "pp":
|
||||
yield from compatible_tags(interpreter="pp3")
|
||||
if interp_name == 'pp':
|
||||
yield from compatible_tags(interpreter='pp3')
|
||||
else:
|
||||
yield from compatible_tags()
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import FrozenSet, NewType, Tuple, Union, cast
|
||||
from typing import cast
|
||||
from typing import FrozenSet
|
||||
from typing import NewType
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from .tags import Tag, parse_tag
|
||||
from .version import InvalidVersion, Version
|
||||
from .tags import parse_tag
|
||||
from .tags import Tag
|
||||
from .version import InvalidVersion
|
||||
from .version import Version
|
||||
|
||||
BuildTag = Union[Tuple[()], Tuple[int, str]]
|
||||
NormalizedName = NewType("NormalizedName", str)
|
||||
NormalizedName = NewType('NormalizedName', str)
|
||||
|
||||
|
||||
class InvalidWheelFilename(ValueError):
|
||||
|
|
@ -24,18 +31,18 @@ class InvalidSdistFilename(ValueError):
|
|||
"""
|
||||
|
||||
|
||||
_canonicalize_regex = re.compile(r"[-_.]+")
|
||||
_canonicalize_regex = re.compile(r'[-_.]+')
|
||||
# PEP 427: The build number must start with a digit.
|
||||
_build_tag_regex = re.compile(r"(\d+)(.*)")
|
||||
_build_tag_regex = re.compile(r'(\d+)(.*)')
|
||||
|
||||
|
||||
def canonicalize_name(name: str) -> NormalizedName:
|
||||
# This is taken from PEP 503.
|
||||
value = _canonicalize_regex.sub("-", name).lower()
|
||||
value = _canonicalize_regex.sub('-', name).lower()
|
||||
return cast(NormalizedName, value)
|
||||
|
||||
|
||||
def canonicalize_version(version: Union[Version, str]) -> str:
|
||||
def canonicalize_version(version: Version | str) -> str:
|
||||
"""
|
||||
This is very similar to Version.__str__, but has one subtle difference
|
||||
with the way it handles the release segment.
|
||||
|
|
@ -53,51 +60,51 @@ def canonicalize_version(version: Union[Version, str]) -> str:
|
|||
|
||||
# Epoch
|
||||
if parsed.epoch != 0:
|
||||
parts.append(f"{parsed.epoch}!")
|
||||
parts.append(f'{parsed.epoch}!')
|
||||
|
||||
# Release segment
|
||||
# NB: This strips trailing '.0's to normalize
|
||||
parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release)))
|
||||
parts.append(re.sub(r'(\.0)+$', '', '.'.join(str(x) for x in parsed.release)))
|
||||
|
||||
# Pre-release
|
||||
if parsed.pre is not None:
|
||||
parts.append("".join(str(x) for x in parsed.pre))
|
||||
parts.append(''.join(str(x) for x in parsed.pre))
|
||||
|
||||
# Post-release
|
||||
if parsed.post is not None:
|
||||
parts.append(f".post{parsed.post}")
|
||||
parts.append(f'.post{parsed.post}')
|
||||
|
||||
# Development release
|
||||
if parsed.dev is not None:
|
||||
parts.append(f".dev{parsed.dev}")
|
||||
parts.append(f'.dev{parsed.dev}')
|
||||
|
||||
# Local version segment
|
||||
if parsed.local is not None:
|
||||
parts.append(f"+{parsed.local}")
|
||||
parts.append(f'+{parsed.local}')
|
||||
|
||||
return "".join(parts)
|
||||
return ''.join(parts)
|
||||
|
||||
|
||||
def parse_wheel_filename(
|
||||
filename: str,
|
||||
) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
|
||||
if not filename.endswith(".whl"):
|
||||
) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
|
||||
if not filename.endswith('.whl'):
|
||||
raise InvalidWheelFilename(
|
||||
f"Invalid wheel filename (extension must be '.whl'): {filename}"
|
||||
f"Invalid wheel filename (extension must be '.whl'): {filename}",
|
||||
)
|
||||
|
||||
filename = filename[:-4]
|
||||
dashes = filename.count("-")
|
||||
dashes = filename.count('-')
|
||||
if dashes not in (4, 5):
|
||||
raise InvalidWheelFilename(
|
||||
f"Invalid wheel filename (wrong number of parts): {filename}"
|
||||
f'Invalid wheel filename (wrong number of parts): {filename}',
|
||||
)
|
||||
|
||||
parts = filename.split("-", dashes - 2)
|
||||
parts = filename.split('-', dashes - 2)
|
||||
name_part = parts[0]
|
||||
# See PEP 427 for the rules on escaping the project name
|
||||
if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
|
||||
raise InvalidWheelFilename(f"Invalid project name: {filename}")
|
||||
if '__' in name_part or re.match(r'^[\w\d._]*$', name_part, re.UNICODE) is None:
|
||||
raise InvalidWheelFilename(f'Invalid project name: {filename}')
|
||||
name = canonicalize_name(name_part)
|
||||
version = Version(parts[1])
|
||||
if dashes == 5:
|
||||
|
|
@ -105,7 +112,7 @@ def parse_wheel_filename(
|
|||
build_match = _build_tag_regex.match(build_part)
|
||||
if build_match is None:
|
||||
raise InvalidWheelFilename(
|
||||
f"Invalid build number: {build_part} in '{filename}'"
|
||||
f"Invalid build number: {build_part} in '{filename}'",
|
||||
)
|
||||
build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
|
||||
else:
|
||||
|
|
@ -114,22 +121,22 @@ def parse_wheel_filename(
|
|||
return (name, version, build, tags)
|
||||
|
||||
|
||||
def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]:
|
||||
if filename.endswith(".tar.gz"):
|
||||
file_stem = filename[: -len(".tar.gz")]
|
||||
elif filename.endswith(".zip"):
|
||||
file_stem = filename[: -len(".zip")]
|
||||
def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
|
||||
if filename.endswith('.tar.gz'):
|
||||
file_stem = filename[: -len('.tar.gz')]
|
||||
elif filename.endswith('.zip'):
|
||||
file_stem = filename[: -len('.zip')]
|
||||
else:
|
||||
raise InvalidSdistFilename(
|
||||
f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
|
||||
f" {filename}"
|
||||
f' {filename}',
|
||||
)
|
||||
|
||||
# We are requiring a PEP 440 version, which cannot contain dashes,
|
||||
# so we split on the last dash.
|
||||
name_part, sep, version_part = file_stem.rpartition("-")
|
||||
name_part, sep, version_part = file_stem.rpartition('-')
|
||||
if not sep:
|
||||
raise InvalidSdistFilename(f"Invalid sdist filename: {filename}")
|
||||
raise InvalidSdistFilename(f'Invalid sdist filename: {filename}')
|
||||
|
||||
name = canonicalize_name(name_part)
|
||||
version = Version(version_part)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,26 @@
|
|||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import itertools
|
||||
import re
|
||||
import warnings
|
||||
from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
|
||||
from typing import Callable
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import SupportsInt
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
|
||||
from ._structures import Infinity
|
||||
from ._structures import InfinityType
|
||||
from ._structures import NegativeInfinity
|
||||
from ._structures import NegativeInfinityType
|
||||
|
||||
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
|
||||
__all__ = ['parse', 'Version', 'LegacyVersion', 'InvalidVersion', 'VERSION_PATTERN']
|
||||
|
||||
InfiniteTypes = Union[InfinityType, NegativeInfinityType]
|
||||
PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
|
||||
|
|
@ -27,19 +37,19 @@ LocalType = Union[
|
|||
],
|
||||
]
|
||||
CmpKey = Tuple[
|
||||
int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
|
||||
int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType,
|
||||
]
|
||||
LegacyCmpKey = Tuple[int, Tuple[str, ...]]
|
||||
VersionComparisonMethod = Callable[
|
||||
[Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
|
||||
[Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool,
|
||||
]
|
||||
|
||||
_Version = collections.namedtuple(
|
||||
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
|
||||
'_Version', ['epoch', 'release', 'dev', 'pre', 'post', 'local'],
|
||||
)
|
||||
|
||||
|
||||
def parse(version: str) -> Union["LegacyVersion", "Version"]:
|
||||
def parse(version: str) -> LegacyVersion | Version:
|
||||
"""
|
||||
Parse the given version string and return either a :class:`Version` object
|
||||
or a :class:`LegacyVersion` object depending on if the given version is
|
||||
|
|
@ -58,7 +68,7 @@ class InvalidVersion(ValueError):
|
|||
|
||||
|
||||
class _BaseVersion:
|
||||
_key: Union[CmpKey, LegacyCmpKey]
|
||||
_key: CmpKey | LegacyCmpKey
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self._key)
|
||||
|
|
@ -66,13 +76,13 @@ class _BaseVersion:
|
|||
# Please keep the duplicated `isinstance` check
|
||||
# in the six comparisons hereunder
|
||||
# unless you find a way to avoid adding overhead function calls.
|
||||
def __lt__(self, other: "_BaseVersion") -> bool:
|
||||
def __lt__(self, other: _BaseVersion) -> bool:
|
||||
if not isinstance(other, _BaseVersion):
|
||||
return NotImplemented
|
||||
|
||||
return self._key < other._key
|
||||
|
||||
def __le__(self, other: "_BaseVersion") -> bool:
|
||||
def __le__(self, other: _BaseVersion) -> bool:
|
||||
if not isinstance(other, _BaseVersion):
|
||||
return NotImplemented
|
||||
|
||||
|
|
@ -84,13 +94,13 @@ class _BaseVersion:
|
|||
|
||||
return self._key == other._key
|
||||
|
||||
def __ge__(self, other: "_BaseVersion") -> bool:
|
||||
def __ge__(self, other: _BaseVersion) -> bool:
|
||||
if not isinstance(other, _BaseVersion):
|
||||
return NotImplemented
|
||||
|
||||
return self._key >= other._key
|
||||
|
||||
def __gt__(self, other: "_BaseVersion") -> bool:
|
||||
def __gt__(self, other: _BaseVersion) -> bool:
|
||||
if not isinstance(other, _BaseVersion):
|
||||
return NotImplemented
|
||||
|
||||
|
|
@ -109,8 +119,8 @@ class LegacyVersion(_BaseVersion):
|
|||
self._key = _legacy_cmpkey(self._version)
|
||||
|
||||
warnings.warn(
|
||||
"Creating a LegacyVersion has been deprecated and will be "
|
||||
"removed in the next major release",
|
||||
'Creating a LegacyVersion has been deprecated and will be '
|
||||
'removed in the next major release',
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
|
|
@ -165,14 +175,14 @@ class LegacyVersion(_BaseVersion):
|
|||
return False
|
||||
|
||||
|
||||
_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
|
||||
_legacy_version_component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
|
||||
|
||||
_legacy_version_replacement_map = {
|
||||
"pre": "c",
|
||||
"preview": "c",
|
||||
"-": "final-",
|
||||
"rc": "c",
|
||||
"dev": "@",
|
||||
'pre': 'c',
|
||||
'preview': 'c',
|
||||
'-': 'final-',
|
||||
'rc': 'c',
|
||||
'dev': '@',
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -180,17 +190,17 @@ def _parse_version_parts(s: str) -> Iterator[str]:
|
|||
for part in _legacy_version_component_re.split(s):
|
||||
part = _legacy_version_replacement_map.get(part, part)
|
||||
|
||||
if not part or part == ".":
|
||||
if not part or part == '.':
|
||||
continue
|
||||
|
||||
if part[:1] in "0123456789":
|
||||
if part[:1] in '0123456789':
|
||||
# pad for numeric comparison
|
||||
yield part.zfill(8)
|
||||
else:
|
||||
yield "*" + part
|
||||
yield '*' + part
|
||||
|
||||
# ensure that alpha/beta/candidate are before final
|
||||
yield "*final"
|
||||
yield '*final'
|
||||
|
||||
|
||||
def _legacy_cmpkey(version: str) -> LegacyCmpKey:
|
||||
|
|
@ -203,16 +213,16 @@ def _legacy_cmpkey(version: str) -> LegacyCmpKey:
|
|||
|
||||
# This scheme is taken from pkg_resources.parse_version setuptools prior to
|
||||
# it's adoption of the packaging library.
|
||||
parts: List[str] = []
|
||||
parts: list[str] = []
|
||||
for part in _parse_version_parts(version.lower()):
|
||||
if part.startswith("*"):
|
||||
if part.startswith('*'):
|
||||
# remove "-" before a prerelease tag
|
||||
if part < "*final":
|
||||
while parts and parts[-1] == "*final-":
|
||||
if part < '*final':
|
||||
while parts and parts[-1] == '*final-':
|
||||
parts.pop()
|
||||
|
||||
# remove trailing zeros from each series of numeric parts
|
||||
while parts and parts[-1] == "00000000":
|
||||
while parts and parts[-1] == '00000000':
|
||||
parts.pop()
|
||||
|
||||
parts.append(part)
|
||||
|
|
@ -256,7 +266,7 @@ VERSION_PATTERN = r"""
|
|||
|
||||
class Version(_BaseVersion):
|
||||
|
||||
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
_regex = re.compile(r'^\s*' + VERSION_PATTERN + r'\s*$', re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
def __init__(self, version: str) -> None:
|
||||
|
||||
|
|
@ -267,14 +277,14 @@ class Version(_BaseVersion):
|
|||
|
||||
# Store the parsed out pieces of the version
|
||||
self._version = _Version(
|
||||
epoch=int(match.group("epoch")) if match.group("epoch") else 0,
|
||||
release=tuple(int(i) for i in match.group("release").split(".")),
|
||||
pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
|
||||
epoch=int(match.group('epoch')) if match.group('epoch') else 0,
|
||||
release=tuple(int(i) for i in match.group('release').split('.')),
|
||||
pre=_parse_letter_version(match.group('pre_l'), match.group('pre_n')),
|
||||
post=_parse_letter_version(
|
||||
match.group("post_l"), match.group("post_n1") or match.group("post_n2")
|
||||
match.group('post_l'), match.group('post_n1') or match.group('post_n2'),
|
||||
),
|
||||
dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
|
||||
local=_parse_local_version(match.group("local")),
|
||||
dev=_parse_letter_version(match.group('dev_l'), match.group('dev_n')),
|
||||
local=_parse_local_version(match.group('local')),
|
||||
)
|
||||
|
||||
# Generate a key which will be used for sorting
|
||||
|
|
@ -295,28 +305,28 @@ class Version(_BaseVersion):
|
|||
|
||||
# Epoch
|
||||
if self.epoch != 0:
|
||||
parts.append(f"{self.epoch}!")
|
||||
parts.append(f'{self.epoch}!')
|
||||
|
||||
# Release segment
|
||||
parts.append(".".join(str(x) for x in self.release))
|
||||
parts.append('.'.join(str(x) for x in self.release))
|
||||
|
||||
# Pre-release
|
||||
if self.pre is not None:
|
||||
parts.append("".join(str(x) for x in self.pre))
|
||||
parts.append(''.join(str(x) for x in self.pre))
|
||||
|
||||
# Post-release
|
||||
if self.post is not None:
|
||||
parts.append(f".post{self.post}")
|
||||
parts.append(f'.post{self.post}')
|
||||
|
||||
# Development release
|
||||
if self.dev is not None:
|
||||
parts.append(f".dev{self.dev}")
|
||||
parts.append(f'.dev{self.dev}')
|
||||
|
||||
# Local version segment
|
||||
if self.local is not None:
|
||||
parts.append(f"+{self.local}")
|
||||
parts.append(f'+{self.local}')
|
||||
|
||||
return "".join(parts)
|
||||
return ''.join(parts)
|
||||
|
||||
@property
|
||||
def epoch(self) -> int:
|
||||
|
|
@ -324,33 +334,33 @@ class Version(_BaseVersion):
|
|||
return _epoch
|
||||
|
||||
@property
|
||||
def release(self) -> Tuple[int, ...]:
|
||||
_release: Tuple[int, ...] = self._version.release
|
||||
def release(self) -> tuple[int, ...]:
|
||||
_release: tuple[int, ...] = self._version.release
|
||||
return _release
|
||||
|
||||
@property
|
||||
def pre(self) -> Optional[Tuple[str, int]]:
|
||||
_pre: Optional[Tuple[str, int]] = self._version.pre
|
||||
def pre(self) -> tuple[str, int] | None:
|
||||
_pre: tuple[str, int] | None = self._version.pre
|
||||
return _pre
|
||||
|
||||
@property
|
||||
def post(self) -> Optional[int]:
|
||||
def post(self) -> int | None:
|
||||
return self._version.post[1] if self._version.post else None
|
||||
|
||||
@property
|
||||
def dev(self) -> Optional[int]:
|
||||
def dev(self) -> int | None:
|
||||
return self._version.dev[1] if self._version.dev else None
|
||||
|
||||
@property
|
||||
def local(self) -> Optional[str]:
|
||||
def local(self) -> str | None:
|
||||
if self._version.local:
|
||||
return ".".join(str(x) for x in self._version.local)
|
||||
return '.'.join(str(x) for x in self._version.local)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def public(self) -> str:
|
||||
return str(self).split("+", 1)[0]
|
||||
return str(self).split('+', 1)[0]
|
||||
|
||||
@property
|
||||
def base_version(self) -> str:
|
||||
|
|
@ -358,12 +368,12 @@ class Version(_BaseVersion):
|
|||
|
||||
# Epoch
|
||||
if self.epoch != 0:
|
||||
parts.append(f"{self.epoch}!")
|
||||
parts.append(f'{self.epoch}!')
|
||||
|
||||
# Release segment
|
||||
parts.append(".".join(str(x) for x in self.release))
|
||||
parts.append('.'.join(str(x) for x in self.release))
|
||||
|
||||
return "".join(parts)
|
||||
return ''.join(parts)
|
||||
|
||||
@property
|
||||
def is_prerelease(self) -> bool:
|
||||
|
|
@ -391,8 +401,8 @@ class Version(_BaseVersion):
|
|||
|
||||
|
||||
def _parse_letter_version(
|
||||
letter: str, number: Union[str, bytes, SupportsInt]
|
||||
) -> Optional[Tuple[str, int]]:
|
||||
letter: str, number: str | bytes | SupportsInt,
|
||||
) -> tuple[str, int] | None:
|
||||
|
||||
if letter:
|
||||
# We consider there to be an implicit 0 in a pre-release if there is
|
||||
|
|
@ -406,30 +416,30 @@ def _parse_letter_version(
|
|||
# We consider some words to be alternate spellings of other words and
|
||||
# in those cases we want to normalize the spellings to our preferred
|
||||
# spelling.
|
||||
if letter == "alpha":
|
||||
letter = "a"
|
||||
elif letter == "beta":
|
||||
letter = "b"
|
||||
elif letter in ["c", "pre", "preview"]:
|
||||
letter = "rc"
|
||||
elif letter in ["rev", "r"]:
|
||||
letter = "post"
|
||||
if letter == 'alpha':
|
||||
letter = 'a'
|
||||
elif letter == 'beta':
|
||||
letter = 'b'
|
||||
elif letter in ['c', 'pre', 'preview']:
|
||||
letter = 'rc'
|
||||
elif letter in ['rev', 'r']:
|
||||
letter = 'post'
|
||||
|
||||
return letter, int(number)
|
||||
if not letter and number:
|
||||
# We assume if we are given a number, but we are not given a letter
|
||||
# then this is using the implicit post release syntax (e.g. 1.0-1)
|
||||
letter = "post"
|
||||
letter = 'post'
|
||||
|
||||
return letter, int(number)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
_local_version_separators = re.compile(r"[\._-]")
|
||||
_local_version_separators = re.compile(r'[\._-]')
|
||||
|
||||
|
||||
def _parse_local_version(local: str) -> Optional[LocalType]:
|
||||
def _parse_local_version(local: str) -> LocalType | None:
|
||||
"""
|
||||
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
|
||||
"""
|
||||
|
|
@ -443,11 +453,11 @@ def _parse_local_version(local: str) -> Optional[LocalType]:
|
|||
|
||||
def _cmpkey(
|
||||
epoch: int,
|
||||
release: Tuple[int, ...],
|
||||
pre: Optional[Tuple[str, int]],
|
||||
post: Optional[Tuple[str, int]],
|
||||
dev: Optional[Tuple[str, int]],
|
||||
local: Optional[Tuple[SubLocalType]],
|
||||
release: tuple[int, ...],
|
||||
pre: tuple[str, int] | None,
|
||||
post: tuple[str, int] | None,
|
||||
dev: tuple[str, int] | None,
|
||||
local: tuple[SubLocalType] | None,
|
||||
) -> CmpKey:
|
||||
|
||||
# When we compare a release version, we want to compare it with all of the
|
||||
|
|
@ -456,7 +466,7 @@ def _cmpkey(
|
|||
# re-reverse it back into the correct order and make it a tuple and use
|
||||
# that for our sorting key.
|
||||
_release = tuple(
|
||||
reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
|
||||
reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))),
|
||||
)
|
||||
|
||||
# We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
|
||||
|
|
@ -498,7 +508,7 @@ def _cmpkey(
|
|||
# - Shorter versions sort before longer versions when the prefixes
|
||||
# match exactly
|
||||
_local = tuple(
|
||||
(i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
|
||||
(i, '') if isinstance(i, int) else (NegativeInfinity, i) for i in local
|
||||
)
|
||||
|
||||
return epoch, _release, _pre, _post, _dev, _local
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue