mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-15 09:42:57 -07:00
Bump packaging from 24.0 to 24.1 (#2347)
* Bump packaging from 24.0 to 24.1 Bumps [packaging](https://github.com/pypa/packaging) from 24.0 to 24.1. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/24.0...24.1) --- updated-dependencies: - dependency-name: packaging dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update packaging==24.1 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
This commit is contained in:
parent
28ad2716ba
commit
2f1607b96b
14 changed files with 288 additions and 238 deletions
|
@ -6,7 +6,7 @@ __title__ = "packaging"
|
||||||
__summary__ = "Core utilities for Python packages"
|
__summary__ = "Core utilities for Python packages"
|
||||||
__uri__ = "https://github.com/pypa/packaging"
|
__uri__ = "https://github.com/pypa/packaging"
|
||||||
|
|
||||||
__version__ = "24.0"
|
__version__ = "24.1"
|
||||||
|
|
||||||
__author__ = "Donald Stufft and individual contributors"
|
__author__ = "Donald Stufft and individual contributors"
|
||||||
__email__ = "donald@stufft.io"
|
__email__ = "donald@stufft.io"
|
||||||
|
|
|
@ -8,10 +8,12 @@ Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
|
||||||
ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
|
ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
from typing import IO, Optional, Tuple
|
from typing import IO
|
||||||
|
|
||||||
|
|
||||||
class ELFInvalid(ValueError):
|
class ELFInvalid(ValueError):
|
||||||
|
@ -87,11 +89,11 @@ class ELFFile:
|
||||||
except struct.error as e:
|
except struct.error as e:
|
||||||
raise ELFInvalid("unable to parse machine and section information") from e
|
raise ELFInvalid("unable to parse machine and section information") from e
|
||||||
|
|
||||||
def _read(self, fmt: str) -> Tuple[int, ...]:
|
def _read(self, fmt: str) -> tuple[int, ...]:
|
||||||
return struct.unpack(fmt, self._f.read(struct.calcsize(fmt)))
|
return struct.unpack(fmt, self._f.read(struct.calcsize(fmt)))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def interpreter(self) -> Optional[str]:
|
def interpreter(self) -> str | None:
|
||||||
"""
|
"""
|
||||||
The path recorded in the ``PT_INTERP`` section header.
|
The path recorded in the ``PT_INTERP`` section header.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
|
@ -5,7 +7,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Dict, Generator, Iterator, NamedTuple, Optional, Sequence, Tuple
|
from typing import Generator, Iterator, NamedTuple, Sequence
|
||||||
|
|
||||||
from ._elffile import EIClass, EIData, ELFFile, EMachine
|
from ._elffile import EIClass, EIData, ELFFile, EMachine
|
||||||
|
|
||||||
|
@ -17,7 +19,7 @@ EF_ARM_ABI_FLOAT_HARD = 0x00000400
|
||||||
# `os.PathLike` not a generic type until Python 3.9, so sticking with `str`
|
# `os.PathLike` not a generic type until Python 3.9, so sticking with `str`
|
||||||
# as the type for `path` until then.
|
# as the type for `path` until then.
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]:
|
def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]:
|
||||||
try:
|
try:
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
yield ELFFile(f)
|
yield ELFFile(f)
|
||||||
|
@ -72,7 +74,7 @@ def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool:
|
||||||
# For now, guess what the highest minor version might be, assume it will
|
# For now, guess what the highest minor version might be, assume it will
|
||||||
# be 50 for testing. Once this actually happens, update the dictionary
|
# be 50 for testing. Once this actually happens, update the dictionary
|
||||||
# with the actual value.
|
# 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):
|
class _GLibCVersion(NamedTuple):
|
||||||
|
@ -80,7 +82,7 @@ class _GLibCVersion(NamedTuple):
|
||||||
minor: int
|
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.
|
Primary implementation of glibc_version_string using os.confstr.
|
||||||
"""
|
"""
|
||||||
|
@ -90,7 +92,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
|
||||||
# https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
|
# https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
|
||||||
try:
|
try:
|
||||||
# Should be a string like "glibc 2.17".
|
# Should be a string like "glibc 2.17".
|
||||||
version_string: Optional[str] = os.confstr("CS_GNU_LIBC_VERSION")
|
version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION")
|
||||||
assert version_string is not None
|
assert version_string is not None
|
||||||
_, version = version_string.rsplit()
|
_, version = version_string.rsplit()
|
||||||
except (AssertionError, AttributeError, OSError, ValueError):
|
except (AssertionError, AttributeError, OSError, ValueError):
|
||||||
|
@ -99,7 +101,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
def _glibc_version_string_ctypes() -> Optional[str]:
|
def _glibc_version_string_ctypes() -> str | None:
|
||||||
"""
|
"""
|
||||||
Fallback implementation of glibc_version_string using ctypes.
|
Fallback implementation of glibc_version_string using ctypes.
|
||||||
"""
|
"""
|
||||||
|
@ -143,12 +145,12 @@ def _glibc_version_string_ctypes() -> Optional[str]:
|
||||||
return version_str
|
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."""
|
"""Returns glibc version string, or None if not using glibc."""
|
||||||
return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
|
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.
|
"""Parse glibc version.
|
||||||
|
|
||||||
We use a regexp instead of str.split because we want to discard any
|
We use a regexp instead of str.split because we want to discard any
|
||||||
|
@ -167,8 +169,8 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
|
||||||
return int(m.group("major")), int(m.group("minor"))
|
return int(m.group("major")), int(m.group("minor"))
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache()
|
@functools.lru_cache
|
||||||
def _get_glibc_version() -> Tuple[int, int]:
|
def _get_glibc_version() -> tuple[int, int]:
|
||||||
version_str = _glibc_version_string()
|
version_str = _glibc_version_string()
|
||||||
if version_str is None:
|
if version_str is None:
|
||||||
return (-1, -1)
|
return (-1, -1)
|
||||||
|
|
|
@ -4,11 +4,13 @@ This module implements logic to detect if the currently running Python is
|
||||||
linked against musl, and what musl version is used.
|
linked against musl, and what musl version is used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Iterator, NamedTuple, Optional, Sequence
|
from typing import Iterator, NamedTuple, Sequence
|
||||||
|
|
||||||
from ._elffile import ELFFile
|
from ._elffile import ELFFile
|
||||||
|
|
||||||
|
@ -18,7 +20,7 @@ class _MuslVersion(NamedTuple):
|
||||||
minor: int
|
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]
|
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
|
return None
|
||||||
|
@ -28,8 +30,8 @@ def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
|
||||||
return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
|
return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache()
|
@functools.lru_cache
|
||||||
def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
|
def _get_musl_version(executable: str) -> _MuslVersion | None:
|
||||||
"""Detect currently-running musl runtime version.
|
"""Detect currently-running musl runtime version.
|
||||||
|
|
||||||
This is done by checking the specified executable's dynamic linking
|
This is done by checking the specified executable's dynamic linking
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
"""Handwritten parser of dependency specifiers.
|
"""Handwritten parser of dependency specifiers.
|
||||||
|
|
||||||
The docstring for each __parse_* function contains ENBF-inspired grammar representing
|
The docstring for each __parse_* function contains EBNF-inspired grammar representing
|
||||||
the implementation.
|
the implementation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
from typing import Any, List, NamedTuple, Optional, Tuple, Union
|
from typing import NamedTuple, Sequence, Tuple, Union
|
||||||
|
|
||||||
from ._tokenizer import DEFAULT_RULES, Tokenizer
|
from ._tokenizer import DEFAULT_RULES, Tokenizer
|
||||||
|
|
||||||
|
@ -41,20 +43,16 @@ class Op(Node):
|
||||||
|
|
||||||
MarkerVar = Union[Variable, Value]
|
MarkerVar = Union[Variable, Value]
|
||||||
MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
|
MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
|
||||||
# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]]
|
MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]]
|
||||||
# MarkerList = List[Union["MarkerList", MarkerAtom, str]]
|
MarkerList = Sequence[Union["MarkerList", MarkerAtom, str]]
|
||||||
# mypy does not support recursive type definition
|
|
||||||
# https://github.com/python/mypy/issues/731
|
|
||||||
MarkerAtom = Any
|
|
||||||
MarkerList = List[Any]
|
|
||||||
|
|
||||||
|
|
||||||
class ParsedRequirement(NamedTuple):
|
class ParsedRequirement(NamedTuple):
|
||||||
name: str
|
name: str
|
||||||
url: str
|
url: str
|
||||||
extras: List[str]
|
extras: list[str]
|
||||||
specifier: str
|
specifier: str
|
||||||
marker: Optional[MarkerList]
|
marker: MarkerList | None
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
|
@ -87,7 +85,7 @@ def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement:
|
||||||
|
|
||||||
def _parse_requirement_details(
|
def _parse_requirement_details(
|
||||||
tokenizer: Tokenizer,
|
tokenizer: Tokenizer,
|
||||||
) -> Tuple[str, str, Optional[MarkerList]]:
|
) -> tuple[str, str, MarkerList | None]:
|
||||||
"""
|
"""
|
||||||
requirement_details = AT URL (WS requirement_marker?)?
|
requirement_details = AT URL (WS requirement_marker?)?
|
||||||
| specifier WS? (requirement_marker)?
|
| specifier WS? (requirement_marker)?
|
||||||
|
@ -156,7 +154,7 @@ def _parse_requirement_marker(
|
||||||
return marker
|
return marker
|
||||||
|
|
||||||
|
|
||||||
def _parse_extras(tokenizer: Tokenizer) -> List[str]:
|
def _parse_extras(tokenizer: Tokenizer) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)?
|
extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)?
|
||||||
"""
|
"""
|
||||||
|
@ -175,11 +173,11 @@ def _parse_extras(tokenizer: Tokenizer) -> List[str]:
|
||||||
return extras
|
return extras
|
||||||
|
|
||||||
|
|
||||||
def _parse_extras_list(tokenizer: Tokenizer) -> List[str]:
|
def _parse_extras_list(tokenizer: Tokenizer) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extras_list = identifier (wsp* ',' wsp* identifier)*
|
extras_list = identifier (wsp* ',' wsp* identifier)*
|
||||||
"""
|
"""
|
||||||
extras: List[str] = []
|
extras: list[str] = []
|
||||||
|
|
||||||
if not tokenizer.check("IDENTIFIER"):
|
if not tokenizer.check("IDENTIFIER"):
|
||||||
return extras
|
return extras
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union
|
from typing import Iterator, NoReturn
|
||||||
|
|
||||||
from .specifiers import Specifier
|
from .specifiers import Specifier
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ class ParserSyntaxError(Exception):
|
||||||
message: str,
|
message: str,
|
||||||
*,
|
*,
|
||||||
source: str,
|
source: str,
|
||||||
span: Tuple[int, int],
|
span: tuple[int, int],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.span = span
|
self.span = span
|
||||||
self.message = message
|
self.message = message
|
||||||
|
@ -34,7 +36,7 @@ class ParserSyntaxError(Exception):
|
||||||
return "\n ".join([self.message, self.source, marker])
|
return "\n ".join([self.message, self.source, marker])
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = {
|
DEFAULT_RULES: dict[str, str | re.Pattern[str]] = {
|
||||||
"LEFT_PARENTHESIS": r"\(",
|
"LEFT_PARENTHESIS": r"\(",
|
||||||
"RIGHT_PARENTHESIS": r"\)",
|
"RIGHT_PARENTHESIS": r"\)",
|
||||||
"LEFT_BRACKET": r"\[",
|
"LEFT_BRACKET": r"\[",
|
||||||
|
@ -96,13 +98,13 @@ class Tokenizer:
|
||||||
self,
|
self,
|
||||||
source: str,
|
source: str,
|
||||||
*,
|
*,
|
||||||
rules: "Dict[str, Union[str, re.Pattern[str]]]",
|
rules: dict[str, str | re.Pattern[str]],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.source = source
|
self.source = source
|
||||||
self.rules: Dict[str, re.Pattern[str]] = {
|
self.rules: dict[str, re.Pattern[str]] = {
|
||||||
name: re.compile(pattern) for name, pattern in rules.items()
|
name: re.compile(pattern) for name, pattern in rules.items()
|
||||||
}
|
}
|
||||||
self.next_token: Optional[Token] = None
|
self.next_token: Token | None = None
|
||||||
self.position = 0
|
self.position = 0
|
||||||
|
|
||||||
def consume(self, name: str) -> None:
|
def consume(self, name: str) -> None:
|
||||||
|
@ -154,8 +156,8 @@ class Tokenizer:
|
||||||
self,
|
self,
|
||||||
message: str,
|
message: str,
|
||||||
*,
|
*,
|
||||||
span_start: Optional[int] = None,
|
span_start: int | None = None,
|
||||||
span_end: Optional[int] = None,
|
span_end: int | None = None,
|
||||||
) -> NoReturn:
|
) -> NoReturn:
|
||||||
"""Raise ParserSyntaxError at the given position."""
|
"""Raise ParserSyntaxError at the given position."""
|
||||||
span = (
|
span = (
|
||||||
|
|
|
@ -2,20 +2,16 @@
|
||||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||||
# for complete details.
|
# for complete details.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import operator
|
import operator
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, TypedDict, cast
|
||||||
|
|
||||||
from ._parser import (
|
from ._parser import MarkerAtom, MarkerList, Op, Value, Variable
|
||||||
MarkerAtom,
|
from ._parser import parse_marker as _parse_marker
|
||||||
MarkerList,
|
|
||||||
Op,
|
|
||||||
Value,
|
|
||||||
Variable,
|
|
||||||
parse_marker as _parse_marker,
|
|
||||||
)
|
|
||||||
from ._tokenizer import ParserSyntaxError
|
from ._tokenizer import ParserSyntaxError
|
||||||
from .specifiers import InvalidSpecifier, Specifier
|
from .specifiers import InvalidSpecifier, Specifier
|
||||||
from .utils import canonicalize_name
|
from .utils import canonicalize_name
|
||||||
|
@ -50,6 +46,78 @@ class UndefinedEnvironmentName(ValueError):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(TypedDict):
|
||||||
|
implementation_name: str
|
||||||
|
"""The implementation's identifier, e.g. ``'cpython'``."""
|
||||||
|
|
||||||
|
implementation_version: str
|
||||||
|
"""
|
||||||
|
The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or
|
||||||
|
``'7.3.13'`` for PyPy3.10 v7.3.13.
|
||||||
|
"""
|
||||||
|
|
||||||
|
os_name: str
|
||||||
|
"""
|
||||||
|
The value of :py:data:`os.name`. The name of the operating system dependent module
|
||||||
|
imported, e.g. ``'posix'``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
platform_machine: str
|
||||||
|
"""
|
||||||
|
Returns the machine type, e.g. ``'i386'``.
|
||||||
|
|
||||||
|
An empty string if the value cannot be determined.
|
||||||
|
"""
|
||||||
|
|
||||||
|
platform_release: str
|
||||||
|
"""
|
||||||
|
The system's release, e.g. ``'2.2.0'`` or ``'NT'``.
|
||||||
|
|
||||||
|
An empty string if the value cannot be determined.
|
||||||
|
"""
|
||||||
|
|
||||||
|
platform_system: str
|
||||||
|
"""
|
||||||
|
The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``.
|
||||||
|
|
||||||
|
An empty string if the value cannot be determined.
|
||||||
|
"""
|
||||||
|
|
||||||
|
platform_version: str
|
||||||
|
"""
|
||||||
|
The system's release version, e.g. ``'#3 on degas'``.
|
||||||
|
|
||||||
|
An empty string if the value cannot be determined.
|
||||||
|
"""
|
||||||
|
|
||||||
|
python_full_version: str
|
||||||
|
"""
|
||||||
|
The Python version as string ``'major.minor.patchlevel'``.
|
||||||
|
|
||||||
|
Note that unlike the Python :py:data:`sys.version`, this value will always include
|
||||||
|
the patchlevel (it defaults to 0).
|
||||||
|
"""
|
||||||
|
|
||||||
|
platform_python_implementation: str
|
||||||
|
"""
|
||||||
|
A string identifying the Python implementation, e.g. ``'CPython'``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
python_version: str
|
||||||
|
"""The Python version as string ``'major.minor'``."""
|
||||||
|
|
||||||
|
sys_platform: str
|
||||||
|
"""
|
||||||
|
This string contains a platform identifier that can be used to append
|
||||||
|
platform-specific components to :py:data:`sys.path`, for instance.
|
||||||
|
|
||||||
|
For Unix systems, except on Linux and AIX, this is the lowercased OS name as
|
||||||
|
returned by ``uname -s`` with the first part of the version as returned by
|
||||||
|
``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python
|
||||||
|
was built.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _normalize_extra_values(results: Any) -> Any:
|
def _normalize_extra_values(results: Any) -> Any:
|
||||||
"""
|
"""
|
||||||
Normalize extra values.
|
Normalize extra values.
|
||||||
|
@ -67,9 +135,8 @@ def _normalize_extra_values(results: Any) -> Any:
|
||||||
|
|
||||||
|
|
||||||
def _format_marker(
|
def _format_marker(
|
||||||
marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True
|
marker: list[str] | MarkerAtom | str, first: bool | None = True
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
||||||
assert isinstance(marker, (list, tuple, str))
|
assert isinstance(marker, (list, tuple, str))
|
||||||
|
|
||||||
# Sometimes we have a structure like [[...]] which is a single item list
|
# Sometimes we have a structure like [[...]] which is a single item list
|
||||||
|
@ -95,7 +162,7 @@ def _format_marker(
|
||||||
return marker
|
return marker
|
||||||
|
|
||||||
|
|
||||||
_operators: Dict[str, Operator] = {
|
_operators: dict[str, Operator] = {
|
||||||
"in": lambda lhs, rhs: lhs in rhs,
|
"in": lambda lhs, rhs: lhs in rhs,
|
||||||
"not in": lambda lhs, rhs: lhs not in rhs,
|
"not in": lambda lhs, rhs: lhs not in rhs,
|
||||||
"<": operator.lt,
|
"<": operator.lt,
|
||||||
|
@ -115,14 +182,14 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
|
||||||
else:
|
else:
|
||||||
return spec.contains(lhs, prereleases=True)
|
return spec.contains(lhs, prereleases=True)
|
||||||
|
|
||||||
oper: Optional[Operator] = _operators.get(op.serialize())
|
oper: Operator | None = _operators.get(op.serialize())
|
||||||
if oper is None:
|
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)
|
return oper(lhs, rhs)
|
||||||
|
|
||||||
|
|
||||||
def _normalize(*values: str, key: str) -> Tuple[str, ...]:
|
def _normalize(*values: str, key: str) -> tuple[str, ...]:
|
||||||
# PEP 685 – Comparison of extra names for optional distribution dependencies
|
# PEP 685 – Comparison of extra names for optional distribution dependencies
|
||||||
# https://peps.python.org/pep-0685/
|
# https://peps.python.org/pep-0685/
|
||||||
# > When comparing extra names, tools MUST normalize the names being
|
# > When comparing extra names, tools MUST normalize the names being
|
||||||
|
@ -134,8 +201,8 @@ def _normalize(*values: str, key: str) -> Tuple[str, ...]:
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
|
def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool:
|
||||||
groups: List[List[bool]] = [[]]
|
groups: list[list[bool]] = [[]]
|
||||||
|
|
||||||
for marker in markers:
|
for marker in markers:
|
||||||
assert isinstance(marker, (list, tuple, str))
|
assert isinstance(marker, (list, tuple, str))
|
||||||
|
@ -164,7 +231,7 @@ def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
|
||||||
return any(all(item) for item in groups)
|
return any(all(item) for item in groups)
|
||||||
|
|
||||||
|
|
||||||
def format_full_version(info: "sys._version_info") -> str:
|
def format_full_version(info: sys._version_info) -> str:
|
||||||
version = "{0.major}.{0.minor}.{0.micro}".format(info)
|
version = "{0.major}.{0.minor}.{0.micro}".format(info)
|
||||||
kind = info.releaselevel
|
kind = info.releaselevel
|
||||||
if kind != "final":
|
if kind != "final":
|
||||||
|
@ -172,7 +239,7 @@ def format_full_version(info: "sys._version_info") -> str:
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
def default_environment() -> Dict[str, str]:
|
def default_environment() -> Environment:
|
||||||
iver = format_full_version(sys.implementation.version)
|
iver = format_full_version(sys.implementation.version)
|
||||||
implementation_name = sys.implementation.name
|
implementation_name = sys.implementation.name
|
||||||
return {
|
return {
|
||||||
|
@ -231,7 +298,7 @@ class Marker:
|
||||||
|
|
||||||
return str(self) == str(other)
|
return str(self) == str(other)
|
||||||
|
|
||||||
def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
|
def evaluate(self, environment: dict[str, str] | None = None) -> bool:
|
||||||
"""Evaluate a marker.
|
"""Evaluate a marker.
|
||||||
|
|
||||||
Return the boolean from evaluating the given marker against the
|
Return the boolean from evaluating the given marker against the
|
||||||
|
@ -240,8 +307,14 @@ class Marker:
|
||||||
|
|
||||||
The environment is determined from the current Python process.
|
The environment is determined from the current Python process.
|
||||||
"""
|
"""
|
||||||
current_environment = default_environment()
|
current_environment = cast("dict[str, str]", default_environment())
|
||||||
current_environment["extra"] = ""
|
current_environment["extra"] = ""
|
||||||
|
# Work around platform.python_version() returning something that is not PEP 440
|
||||||
|
# compliant for non-tagged Python builds. We preserve default_environment()'s
|
||||||
|
# behavior of returning platform.python_version() verbatim, and leave it to the
|
||||||
|
# caller to provide a syntactically valid version if they want to override it.
|
||||||
|
if current_environment["python_full_version"].endswith("+"):
|
||||||
|
current_environment["python_full_version"] += "local"
|
||||||
if environment is not None:
|
if environment is not None:
|
||||||
current_environment.update(environment)
|
current_environment.update(environment)
|
||||||
# The API used to allow setting extra to None. We need to handle this
|
# The API used to allow setting extra to None. We need to handle this
|
||||||
|
|
|
@ -1,50 +1,31 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import email.feedparser
|
import email.feedparser
|
||||||
import email.header
|
import email.header
|
||||||
import email.message
|
import email.message
|
||||||
import email.parser
|
import email.parser
|
||||||
import email.policy
|
import email.policy
|
||||||
import sys
|
|
||||||
import typing
|
import typing
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
Dict,
|
|
||||||
Generic,
|
Generic,
|
||||||
List,
|
Literal,
|
||||||
Optional,
|
TypedDict,
|
||||||
Tuple,
|
|
||||||
Type,
|
|
||||||
Union,
|
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import requirements, specifiers, utils, version as version_module
|
from . import requirements, specifiers, utils
|
||||||
|
from . import version as version_module
|
||||||
|
|
||||||
T = typing.TypeVar("T")
|
T = typing.TypeVar("T")
|
||||||
if sys.version_info[:2] >= (3, 8): # pragma: no cover
|
|
||||||
from typing import Literal, TypedDict
|
|
||||||
else: # pragma: no cover
|
|
||||||
if typing.TYPE_CHECKING:
|
|
||||||
from typing_extensions import Literal, TypedDict
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
from typing_extensions import Literal, TypedDict
|
|
||||||
except ImportError:
|
|
||||||
|
|
||||||
class Literal:
|
|
||||||
def __init_subclass__(*_args, **_kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TypedDict:
|
|
||||||
def __init_subclass__(*_args, **_kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ExceptionGroup
|
ExceptionGroup
|
||||||
except NameError: # pragma: no cover
|
except NameError: # pragma: no cover
|
||||||
|
|
||||||
class ExceptionGroup(Exception): # noqa: N818
|
class ExceptionGroup(Exception):
|
||||||
"""A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
|
"""A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
|
||||||
|
|
||||||
If :external:exc:`ExceptionGroup` is already defined by Python itself,
|
If :external:exc:`ExceptionGroup` is already defined by Python itself,
|
||||||
|
@ -52,9 +33,9 @@ except NameError: # pragma: no cover
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message: str
|
message: str
|
||||||
exceptions: List[Exception]
|
exceptions: list[Exception]
|
||||||
|
|
||||||
def __init__(self, message: str, exceptions: List[Exception]) -> None:
|
def __init__(self, message: str, exceptions: list[Exception]) -> None:
|
||||||
self.message = message
|
self.message = message
|
||||||
self.exceptions = exceptions
|
self.exceptions = exceptions
|
||||||
|
|
||||||
|
@ -100,32 +81,32 @@ class RawMetadata(TypedDict, total=False):
|
||||||
metadata_version: str
|
metadata_version: str
|
||||||
name: str
|
name: str
|
||||||
version: str
|
version: str
|
||||||
platforms: List[str]
|
platforms: list[str]
|
||||||
summary: str
|
summary: str
|
||||||
description: str
|
description: str
|
||||||
keywords: List[str]
|
keywords: list[str]
|
||||||
home_page: str
|
home_page: str
|
||||||
author: str
|
author: str
|
||||||
author_email: str
|
author_email: str
|
||||||
license: str
|
license: str
|
||||||
|
|
||||||
# Metadata 1.1 - PEP 314
|
# Metadata 1.1 - PEP 314
|
||||||
supported_platforms: List[str]
|
supported_platforms: list[str]
|
||||||
download_url: str
|
download_url: str
|
||||||
classifiers: List[str]
|
classifiers: list[str]
|
||||||
requires: List[str]
|
requires: list[str]
|
||||||
provides: List[str]
|
provides: list[str]
|
||||||
obsoletes: List[str]
|
obsoletes: list[str]
|
||||||
|
|
||||||
# Metadata 1.2 - PEP 345
|
# Metadata 1.2 - PEP 345
|
||||||
maintainer: str
|
maintainer: str
|
||||||
maintainer_email: str
|
maintainer_email: str
|
||||||
requires_dist: List[str]
|
requires_dist: list[str]
|
||||||
provides_dist: List[str]
|
provides_dist: list[str]
|
||||||
obsoletes_dist: List[str]
|
obsoletes_dist: list[str]
|
||||||
requires_python: str
|
requires_python: str
|
||||||
requires_external: List[str]
|
requires_external: list[str]
|
||||||
project_urls: Dict[str, str]
|
project_urls: dict[str, str]
|
||||||
|
|
||||||
# Metadata 2.0
|
# Metadata 2.0
|
||||||
# PEP 426 attempted to completely revamp the metadata format
|
# PEP 426 attempted to completely revamp the metadata format
|
||||||
|
@ -138,10 +119,10 @@ class RawMetadata(TypedDict, total=False):
|
||||||
|
|
||||||
# Metadata 2.1 - PEP 566
|
# Metadata 2.1 - PEP 566
|
||||||
description_content_type: str
|
description_content_type: str
|
||||||
provides_extra: List[str]
|
provides_extra: list[str]
|
||||||
|
|
||||||
# Metadata 2.2 - PEP 643
|
# Metadata 2.2 - PEP 643
|
||||||
dynamic: List[str]
|
dynamic: list[str]
|
||||||
|
|
||||||
# Metadata 2.3 - PEP 685
|
# Metadata 2.3 - PEP 685
|
||||||
# No new fields were added in PEP 685, just some edge case were
|
# No new fields were added in PEP 685, just some edge case were
|
||||||
|
@ -185,12 +166,12 @@ _DICT_FIELDS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_keywords(data: str) -> List[str]:
|
def _parse_keywords(data: str) -> list[str]:
|
||||||
"""Split a string of comma-separate keyboards into a list of keywords."""
|
"""Split a string of comma-separate keyboards into a list of keywords."""
|
||||||
return [k.strip() for k in data.split(",")]
|
return [k.strip() for k in data.split(",")]
|
||||||
|
|
||||||
|
|
||||||
def _parse_project_urls(data: List[str]) -> Dict[str, str]:
|
def _parse_project_urls(data: list[str]) -> dict[str, str]:
|
||||||
"""Parse a list of label/URL string pairings separated by a comma."""
|
"""Parse a list of label/URL string pairings separated by a comma."""
|
||||||
urls = {}
|
urls = {}
|
||||||
for pair in data:
|
for pair in data:
|
||||||
|
@ -230,7 +211,7 @@ def _parse_project_urls(data: List[str]) -> Dict[str, str]:
|
||||||
return urls
|
return urls
|
||||||
|
|
||||||
|
|
||||||
def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str:
|
def _get_payload(msg: email.message.Message, source: bytes | str) -> str:
|
||||||
"""Get the body of the message."""
|
"""Get the body of the message."""
|
||||||
# If our source is a str, then our caller has managed encodings for us,
|
# If our source is a str, then our caller has managed encodings for us,
|
||||||
# and we don't need to deal with it.
|
# and we don't need to deal with it.
|
||||||
|
@ -292,7 +273,7 @@ _EMAIL_TO_RAW_MAPPING = {
|
||||||
_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()}
|
_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()}
|
||||||
|
|
||||||
|
|
||||||
def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[str]]]:
|
def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]:
|
||||||
"""Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``).
|
"""Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``).
|
||||||
|
|
||||||
This function returns a two-item tuple of dicts. The first dict is of
|
This function returns a two-item tuple of dicts. The first dict is of
|
||||||
|
@ -308,8 +289,8 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st
|
||||||
included in this dict.
|
included in this dict.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raw: Dict[str, Union[str, List[str], Dict[str, str]]] = {}
|
raw: dict[str, str | list[str] | dict[str, str]] = {}
|
||||||
unparsed: Dict[str, List[str]] = {}
|
unparsed: dict[str, list[str]] = {}
|
||||||
|
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data)
|
parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data)
|
||||||
|
@ -357,7 +338,7 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st
|
||||||
# The Header object stores it's data as chunks, and each chunk
|
# The Header object stores it's data as chunks, and each chunk
|
||||||
# can be independently encoded, so we'll need to check each
|
# can be independently encoded, so we'll need to check each
|
||||||
# of them.
|
# of them.
|
||||||
chunks: List[Tuple[bytes, Optional[str]]] = []
|
chunks: list[tuple[bytes, str | None]] = []
|
||||||
for bin, encoding in email.header.decode_header(h):
|
for bin, encoding in email.header.decode_header(h):
|
||||||
try:
|
try:
|
||||||
bin.decode("utf8", "strict")
|
bin.decode("utf8", "strict")
|
||||||
|
@ -499,11 +480,11 @@ class _Validator(Generic[T]):
|
||||||
) -> None:
|
) -> None:
|
||||||
self.added = added
|
self.added = added
|
||||||
|
|
||||||
def __set_name__(self, _owner: "Metadata", name: str) -> None:
|
def __set_name__(self, _owner: Metadata, name: str) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.raw_name = _RAW_TO_EMAIL_MAPPING[name]
|
self.raw_name = _RAW_TO_EMAIL_MAPPING[name]
|
||||||
|
|
||||||
def __get__(self, instance: "Metadata", _owner: Type["Metadata"]) -> T:
|
def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T:
|
||||||
# With Python 3.8, the caching can be replaced with functools.cached_property().
|
# With Python 3.8, the caching can be replaced with functools.cached_property().
|
||||||
# No need to check the cache as attribute lookup will resolve into the
|
# No need to check the cache as attribute lookup will resolve into the
|
||||||
# instance's __dict__ before __get__ is called.
|
# instance's __dict__ before __get__ is called.
|
||||||
|
@ -531,7 +512,7 @@ class _Validator(Generic[T]):
|
||||||
return cast(T, value)
|
return cast(T, value)
|
||||||
|
|
||||||
def _invalid_metadata(
|
def _invalid_metadata(
|
||||||
self, msg: str, cause: Optional[Exception] = None
|
self, msg: str, cause: Exception | None = None
|
||||||
) -> InvalidMetadata:
|
) -> InvalidMetadata:
|
||||||
exc = InvalidMetadata(
|
exc = InvalidMetadata(
|
||||||
self.raw_name, msg.format_map({"field": repr(self.raw_name)})
|
self.raw_name, msg.format_map({"field": repr(self.raw_name)})
|
||||||
|
@ -606,7 +587,7 @@ class _Validator(Generic[T]):
|
||||||
)
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _process_dynamic(self, value: List[str]) -> List[str]:
|
def _process_dynamic(self, value: list[str]) -> list[str]:
|
||||||
for dynamic_field in map(str.lower, value):
|
for dynamic_field in map(str.lower, value):
|
||||||
if dynamic_field in {"name", "version", "metadata-version"}:
|
if dynamic_field in {"name", "version", "metadata-version"}:
|
||||||
raise self._invalid_metadata(
|
raise self._invalid_metadata(
|
||||||
|
@ -618,8 +599,8 @@ class _Validator(Generic[T]):
|
||||||
|
|
||||||
def _process_provides_extra(
|
def _process_provides_extra(
|
||||||
self,
|
self,
|
||||||
value: List[str],
|
value: list[str],
|
||||||
) -> List[utils.NormalizedName]:
|
) -> list[utils.NormalizedName]:
|
||||||
normalized_names = []
|
normalized_names = []
|
||||||
try:
|
try:
|
||||||
for name in value:
|
for name in value:
|
||||||
|
@ -641,8 +622,8 @@ class _Validator(Generic[T]):
|
||||||
|
|
||||||
def _process_requires_dist(
|
def _process_requires_dist(
|
||||||
self,
|
self,
|
||||||
value: List[str],
|
value: list[str],
|
||||||
) -> List[requirements.Requirement]:
|
) -> list[requirements.Requirement]:
|
||||||
reqs = []
|
reqs = []
|
||||||
try:
|
try:
|
||||||
for req in value:
|
for req in value:
|
||||||
|
@ -665,7 +646,7 @@ class Metadata:
|
||||||
_raw: RawMetadata
|
_raw: RawMetadata
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> "Metadata":
|
def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata:
|
||||||
"""Create an instance from :class:`RawMetadata`.
|
"""Create an instance from :class:`RawMetadata`.
|
||||||
|
|
||||||
If *validate* is true, all metadata will be validated. All exceptions
|
If *validate* is true, all metadata will be validated. All exceptions
|
||||||
|
@ -675,7 +656,7 @@ class Metadata:
|
||||||
ins._raw = data.copy() # Mutations occur due to caching enriched values.
|
ins._raw = data.copy() # Mutations occur due to caching enriched values.
|
||||||
|
|
||||||
if validate:
|
if validate:
|
||||||
exceptions: List[Exception] = []
|
exceptions: list[Exception] = []
|
||||||
try:
|
try:
|
||||||
metadata_version = ins.metadata_version
|
metadata_version = ins.metadata_version
|
||||||
metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version)
|
metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version)
|
||||||
|
@ -722,9 +703,7 @@ class Metadata:
|
||||||
return ins
|
return ins
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_email(
|
def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
|
||||||
cls, data: Union[bytes, str], *, validate: bool = True
|
|
||||||
) -> "Metadata":
|
|
||||||
"""Parse metadata from email headers.
|
"""Parse metadata from email headers.
|
||||||
|
|
||||||
If *validate* is true, the metadata will be validated. All exceptions
|
If *validate* is true, the metadata will be validated. All exceptions
|
||||||
|
@ -760,66 +739,66 @@ class Metadata:
|
||||||
*validate* parameter)"""
|
*validate* parameter)"""
|
||||||
version: _Validator[version_module.Version] = _Validator()
|
version: _Validator[version_module.Version] = _Validator()
|
||||||
""":external:ref:`core-metadata-version` (required)"""
|
""":external:ref:`core-metadata-version` (required)"""
|
||||||
dynamic: _Validator[Optional[List[str]]] = _Validator(
|
dynamic: _Validator[list[str] | None] = _Validator(
|
||||||
added="2.2",
|
added="2.2",
|
||||||
)
|
)
|
||||||
""":external:ref:`core-metadata-dynamic`
|
""":external:ref:`core-metadata-dynamic`
|
||||||
(validated against core metadata field names and lowercased)"""
|
(validated against core metadata field names and lowercased)"""
|
||||||
platforms: _Validator[Optional[List[str]]] = _Validator()
|
platforms: _Validator[list[str] | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-platform`"""
|
""":external:ref:`core-metadata-platform`"""
|
||||||
supported_platforms: _Validator[Optional[List[str]]] = _Validator(added="1.1")
|
supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1")
|
||||||
""":external:ref:`core-metadata-supported-platform`"""
|
""":external:ref:`core-metadata-supported-platform`"""
|
||||||
summary: _Validator[Optional[str]] = _Validator()
|
summary: _Validator[str | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-summary` (validated to contain no newlines)"""
|
""":external:ref:`core-metadata-summary` (validated to contain no newlines)"""
|
||||||
description: _Validator[Optional[str]] = _Validator() # TODO 2.1: can be in body
|
description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body
|
||||||
""":external:ref:`core-metadata-description`"""
|
""":external:ref:`core-metadata-description`"""
|
||||||
description_content_type: _Validator[Optional[str]] = _Validator(added="2.1")
|
description_content_type: _Validator[str | None] = _Validator(added="2.1")
|
||||||
""":external:ref:`core-metadata-description-content-type` (validated)"""
|
""":external:ref:`core-metadata-description-content-type` (validated)"""
|
||||||
keywords: _Validator[Optional[List[str]]] = _Validator()
|
keywords: _Validator[list[str] | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-keywords`"""
|
""":external:ref:`core-metadata-keywords`"""
|
||||||
home_page: _Validator[Optional[str]] = _Validator()
|
home_page: _Validator[str | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-home-page`"""
|
""":external:ref:`core-metadata-home-page`"""
|
||||||
download_url: _Validator[Optional[str]] = _Validator(added="1.1")
|
download_url: _Validator[str | None] = _Validator(added="1.1")
|
||||||
""":external:ref:`core-metadata-download-url`"""
|
""":external:ref:`core-metadata-download-url`"""
|
||||||
author: _Validator[Optional[str]] = _Validator()
|
author: _Validator[str | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-author`"""
|
""":external:ref:`core-metadata-author`"""
|
||||||
author_email: _Validator[Optional[str]] = _Validator()
|
author_email: _Validator[str | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-author-email`"""
|
""":external:ref:`core-metadata-author-email`"""
|
||||||
maintainer: _Validator[Optional[str]] = _Validator(added="1.2")
|
maintainer: _Validator[str | None] = _Validator(added="1.2")
|
||||||
""":external:ref:`core-metadata-maintainer`"""
|
""":external:ref:`core-metadata-maintainer`"""
|
||||||
maintainer_email: _Validator[Optional[str]] = _Validator(added="1.2")
|
maintainer_email: _Validator[str | None] = _Validator(added="1.2")
|
||||||
""":external:ref:`core-metadata-maintainer-email`"""
|
""":external:ref:`core-metadata-maintainer-email`"""
|
||||||
license: _Validator[Optional[str]] = _Validator()
|
license: _Validator[str | None] = _Validator()
|
||||||
""":external:ref:`core-metadata-license`"""
|
""":external:ref:`core-metadata-license`"""
|
||||||
classifiers: _Validator[Optional[List[str]]] = _Validator(added="1.1")
|
classifiers: _Validator[list[str] | None] = _Validator(added="1.1")
|
||||||
""":external:ref:`core-metadata-classifier`"""
|
""":external:ref:`core-metadata-classifier`"""
|
||||||
requires_dist: _Validator[Optional[List[requirements.Requirement]]] = _Validator(
|
requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(
|
||||||
added="1.2"
|
added="1.2"
|
||||||
)
|
)
|
||||||
""":external:ref:`core-metadata-requires-dist`"""
|
""":external:ref:`core-metadata-requires-dist`"""
|
||||||
requires_python: _Validator[Optional[specifiers.SpecifierSet]] = _Validator(
|
requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator(
|
||||||
added="1.2"
|
added="1.2"
|
||||||
)
|
)
|
||||||
""":external:ref:`core-metadata-requires-python`"""
|
""":external:ref:`core-metadata-requires-python`"""
|
||||||
# Because `Requires-External` allows for non-PEP 440 version specifiers, we
|
# Because `Requires-External` allows for non-PEP 440 version specifiers, we
|
||||||
# don't do any processing on the values.
|
# don't do any processing on the values.
|
||||||
requires_external: _Validator[Optional[List[str]]] = _Validator(added="1.2")
|
requires_external: _Validator[list[str] | None] = _Validator(added="1.2")
|
||||||
""":external:ref:`core-metadata-requires-external`"""
|
""":external:ref:`core-metadata-requires-external`"""
|
||||||
project_urls: _Validator[Optional[Dict[str, str]]] = _Validator(added="1.2")
|
project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2")
|
||||||
""":external:ref:`core-metadata-project-url`"""
|
""":external:ref:`core-metadata-project-url`"""
|
||||||
# PEP 685 lets us raise an error if an extra doesn't pass `Name` validation
|
# PEP 685 lets us raise an error if an extra doesn't pass `Name` validation
|
||||||
# regardless of metadata version.
|
# regardless of metadata version.
|
||||||
provides_extra: _Validator[Optional[List[utils.NormalizedName]]] = _Validator(
|
provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator(
|
||||||
added="2.1",
|
added="2.1",
|
||||||
)
|
)
|
||||||
""":external:ref:`core-metadata-provides-extra`"""
|
""":external:ref:`core-metadata-provides-extra`"""
|
||||||
provides_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2")
|
provides_dist: _Validator[list[str] | None] = _Validator(added="1.2")
|
||||||
""":external:ref:`core-metadata-provides-dist`"""
|
""":external:ref:`core-metadata-provides-dist`"""
|
||||||
obsoletes_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2")
|
obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2")
|
||||||
""":external:ref:`core-metadata-obsoletes-dist`"""
|
""":external:ref:`core-metadata-obsoletes-dist`"""
|
||||||
requires: _Validator[Optional[List[str]]] = _Validator(added="1.1")
|
requires: _Validator[list[str] | None] = _Validator(added="1.1")
|
||||||
"""``Requires`` (deprecated)"""
|
"""``Requires`` (deprecated)"""
|
||||||
provides: _Validator[Optional[List[str]]] = _Validator(added="1.1")
|
provides: _Validator[list[str] | None] = _Validator(added="1.1")
|
||||||
"""``Provides`` (deprecated)"""
|
"""``Provides`` (deprecated)"""
|
||||||
obsoletes: _Validator[Optional[List[str]]] = _Validator(added="1.1")
|
obsoletes: _Validator[list[str] | None] = _Validator(added="1.1")
|
||||||
"""``Obsoletes`` (deprecated)"""
|
"""``Obsoletes`` (deprecated)"""
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# This file is dual licensed under the terms of the Apache License, Version
|
# 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
|
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||||
# for complete details.
|
# for complete details.
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Iterator, Optional, Set
|
from typing import Any, Iterator
|
||||||
|
|
||||||
from ._parser import parse_requirement as _parse_requirement
|
from ._parser import parse_requirement as _parse_requirement
|
||||||
from ._tokenizer import ParserSyntaxError
|
from ._tokenizer import ParserSyntaxError
|
||||||
|
@ -37,10 +38,10 @@ class Requirement:
|
||||||
raise InvalidRequirement(str(e)) from e
|
raise InvalidRequirement(str(e)) from e
|
||||||
|
|
||||||
self.name: str = parsed.name
|
self.name: str = parsed.name
|
||||||
self.url: Optional[str] = parsed.url or None
|
self.url: str | None = parsed.url or None
|
||||||
self.extras: Set[str] = set(parsed.extras or [])
|
self.extras: set[str] = set(parsed.extras or [])
|
||||||
self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
|
self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
|
||||||
self.marker: Optional[Marker] = None
|
self.marker: Marker | None = None
|
||||||
if parsed.marker is not None:
|
if parsed.marker is not None:
|
||||||
self.marker = Marker.__new__(Marker)
|
self.marker = Marker.__new__(Marker)
|
||||||
self.marker._markers = _normalize_extra_values(parsed.marker)
|
self.marker._markers = _normalize_extra_values(parsed.marker)
|
||||||
|
|
|
@ -8,10 +8,12 @@
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union
|
from typing import Callable, Iterable, Iterator, TypeVar, Union
|
||||||
|
|
||||||
from .utils import canonicalize_version
|
from .utils import canonicalize_version
|
||||||
from .version import Version
|
from .version import Version
|
||||||
|
@ -64,7 +66,7 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def prereleases(self) -> Optional[bool]:
|
def prereleases(self) -> bool | None:
|
||||||
"""Whether or not pre-releases as a whole are allowed.
|
"""Whether or not pre-releases as a whole are allowed.
|
||||||
|
|
||||||
This can be set to either ``True`` or ``False`` to explicitly enable or disable
|
This can be set to either ``True`` or ``False`` to explicitly enable or disable
|
||||||
|
@ -79,14 +81,14 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@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.
|
Determines if the given item is contained within this specifier.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def filter(
|
def filter(
|
||||||
self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
|
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
|
||||||
) -> Iterator[UnparsedVersionVar]:
|
) -> Iterator[UnparsedVersionVar]:
|
||||||
"""
|
"""
|
||||||
Takes an iterable of items and filters them so that only items which
|
Takes an iterable of items and filters them so that only items which
|
||||||
|
@ -217,7 +219,7 @@ class Specifier(BaseSpecifier):
|
||||||
"===": "arbitrary",
|
"===": "arbitrary",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
|
def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:
|
||||||
"""Initialize a Specifier instance.
|
"""Initialize a Specifier instance.
|
||||||
|
|
||||||
:param spec:
|
:param spec:
|
||||||
|
@ -234,7 +236,7 @@ class Specifier(BaseSpecifier):
|
||||||
if not match:
|
if not match:
|
||||||
raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
|
raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
|
||||||
|
|
||||||
self._spec: Tuple[str, str] = (
|
self._spec: tuple[str, str] = (
|
||||||
match.group("operator").strip(),
|
match.group("operator").strip(),
|
||||||
match.group("version").strip(),
|
match.group("version").strip(),
|
||||||
)
|
)
|
||||||
|
@ -318,7 +320,7 @@ class Specifier(BaseSpecifier):
|
||||||
return "{}{}".format(*self._spec)
|
return "{}{}".format(*self._spec)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _canonical_spec(self) -> Tuple[str, str]:
|
def _canonical_spec(self) -> tuple[str, str]:
|
||||||
canonical_version = canonicalize_version(
|
canonical_version = canonicalize_version(
|
||||||
self._spec[1],
|
self._spec[1],
|
||||||
strip_trailing_zero=(self._spec[0] != "~="),
|
strip_trailing_zero=(self._spec[0] != "~="),
|
||||||
|
@ -364,7 +366,6 @@ class Specifier(BaseSpecifier):
|
||||||
return operator_callable
|
return operator_callable
|
||||||
|
|
||||||
def _compare_compatible(self, prospective: Version, spec: str) -> bool:
|
def _compare_compatible(self, prospective: Version, spec: str) -> bool:
|
||||||
|
|
||||||
# Compatible releases have an equivalent combination of >= and ==. That
|
# Compatible releases have an equivalent combination of >= and ==. That
|
||||||
# is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
|
# is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
|
||||||
# implement this in terms of the other specifiers instead of
|
# implement this in terms of the other specifiers instead of
|
||||||
|
@ -385,7 +386,6 @@ class Specifier(BaseSpecifier):
|
||||||
)
|
)
|
||||||
|
|
||||||
def _compare_equal(self, prospective: Version, spec: str) -> bool:
|
def _compare_equal(self, prospective: Version, spec: str) -> bool:
|
||||||
|
|
||||||
# We need special logic to handle prefix matching
|
# 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.
|
# In the case of prefix matching we want to ignore local segment.
|
||||||
|
@ -429,21 +429,18 @@ class Specifier(BaseSpecifier):
|
||||||
return not self._compare_equal(prospective, spec)
|
return not self._compare_equal(prospective, spec)
|
||||||
|
|
||||||
def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
|
def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
|
||||||
|
|
||||||
# NB: Local version identifiers are NOT permitted in the version
|
# NB: Local version identifiers are NOT permitted in the version
|
||||||
# specifier, so local version labels can be universally removed from
|
# specifier, so local version labels can be universally removed from
|
||||||
# the prospective version.
|
# the prospective version.
|
||||||
return Version(prospective.public) <= Version(spec)
|
return Version(prospective.public) <= Version(spec)
|
||||||
|
|
||||||
def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
|
def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
|
||||||
|
|
||||||
# NB: Local version identifiers are NOT permitted in the version
|
# NB: Local version identifiers are NOT permitted in the version
|
||||||
# specifier, so local version labels can be universally removed from
|
# specifier, so local version labels can be universally removed from
|
||||||
# the prospective version.
|
# the prospective version.
|
||||||
return Version(prospective.public) >= Version(spec)
|
return Version(prospective.public) >= Version(spec)
|
||||||
|
|
||||||
def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
|
def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
|
||||||
|
|
||||||
# Convert our spec to a Version instance, since we'll want to work with
|
# Convert our spec to a Version instance, since we'll want to work with
|
||||||
# it as a version.
|
# it as a version.
|
||||||
spec = Version(spec_str)
|
spec = Version(spec_str)
|
||||||
|
@ -468,7 +465,6 @@ class Specifier(BaseSpecifier):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
|
def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
|
||||||
|
|
||||||
# Convert our spec to a Version instance, since we'll want to work with
|
# Convert our spec to a Version instance, since we'll want to work with
|
||||||
# it as a version.
|
# it as a version.
|
||||||
spec = Version(spec_str)
|
spec = Version(spec_str)
|
||||||
|
@ -501,7 +497,7 @@ class Specifier(BaseSpecifier):
|
||||||
def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
|
def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
|
||||||
return str(prospective).lower() == str(spec).lower()
|
return str(prospective).lower() == str(spec).lower()
|
||||||
|
|
||||||
def __contains__(self, item: Union[str, Version]) -> bool:
|
def __contains__(self, item: str | Version) -> bool:
|
||||||
"""Return whether or not the item is contained in this specifier.
|
"""Return whether or not the item is contained in this specifier.
|
||||||
|
|
||||||
:param item: The item to check for.
|
:param item: The item to check for.
|
||||||
|
@ -522,9 +518,7 @@ class Specifier(BaseSpecifier):
|
||||||
"""
|
"""
|
||||||
return self.contains(item)
|
return self.contains(item)
|
||||||
|
|
||||||
def contains(
|
def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:
|
||||||
self, item: UnparsedVersion, prereleases: Optional[bool] = None
|
|
||||||
) -> bool:
|
|
||||||
"""Return whether or not the item is contained in this specifier.
|
"""Return whether or not the item is contained in this specifier.
|
||||||
|
|
||||||
:param item:
|
:param item:
|
||||||
|
@ -569,7 +563,7 @@ class Specifier(BaseSpecifier):
|
||||||
return operator_callable(normalized_item, self.version)
|
return operator_callable(normalized_item, self.version)
|
||||||
|
|
||||||
def filter(
|
def filter(
|
||||||
self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
|
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
|
||||||
) -> Iterator[UnparsedVersionVar]:
|
) -> Iterator[UnparsedVersionVar]:
|
||||||
"""Filter items in the given iterable, that match the specifier.
|
"""Filter items in the given iterable, that match the specifier.
|
||||||
|
|
||||||
|
@ -633,7 +627,7 @@ class Specifier(BaseSpecifier):
|
||||||
_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]:
|
def _version_split(version: str) -> list[str]:
|
||||||
"""Split version into components.
|
"""Split version into components.
|
||||||
|
|
||||||
The split components are intended for version comparison. The logic does
|
The split components are intended for version comparison. The logic does
|
||||||
|
@ -641,7 +635,7 @@ def _version_split(version: str) -> List[str]:
|
||||||
components back with :func:`_version_join` may not produce the original
|
components back with :func:`_version_join` may not produce the original
|
||||||
version string.
|
version string.
|
||||||
"""
|
"""
|
||||||
result: List[str] = []
|
result: list[str] = []
|
||||||
|
|
||||||
epoch, _, rest = version.rpartition("!")
|
epoch, _, rest = version.rpartition("!")
|
||||||
result.append(epoch or "0")
|
result.append(epoch or "0")
|
||||||
|
@ -655,7 +649,7 @@ def _version_split(version: str) -> List[str]:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _version_join(components: List[str]) -> str:
|
def _version_join(components: list[str]) -> str:
|
||||||
"""Join split version components into a version string.
|
"""Join split version components into a version string.
|
||||||
|
|
||||||
This function assumes the input came from :func:`_version_split`, where the
|
This function assumes the input came from :func:`_version_split`, where the
|
||||||
|
@ -672,7 +666,7 @@ def _is_not_suffix(segment: str) -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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 = [], []
|
left_split, right_split = [], []
|
||||||
|
|
||||||
# Get the release segment of our versions
|
# Get the release segment of our versions
|
||||||
|
@ -700,9 +694,7 @@ class SpecifierSet(BaseSpecifier):
|
||||||
specifiers (``>=3.0,!=3.1``), or no specifier at all.
|
specifiers (``>=3.0,!=3.1``), or no specifier at all.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> None:
|
||||||
self, specifiers: str = "", prereleases: Optional[bool] = None
|
|
||||||
) -> None:
|
|
||||||
"""Initialize a SpecifierSet instance.
|
"""Initialize a SpecifierSet instance.
|
||||||
|
|
||||||
:param specifiers:
|
:param specifiers:
|
||||||
|
@ -730,7 +722,7 @@ class SpecifierSet(BaseSpecifier):
|
||||||
self._prereleases = prereleases
|
self._prereleases = prereleases
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prereleases(self) -> Optional[bool]:
|
def prereleases(self) -> bool | None:
|
||||||
# If we have been given an explicit prerelease modifier, then we'll
|
# If we have been given an explicit prerelease modifier, then we'll
|
||||||
# pass that through here.
|
# pass that through here.
|
||||||
if self._prereleases is not None:
|
if self._prereleases is not None:
|
||||||
|
@ -787,7 +779,7 @@ class SpecifierSet(BaseSpecifier):
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(self._specs)
|
return hash(self._specs)
|
||||||
|
|
||||||
def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
|
def __and__(self, other: SpecifierSet | str) -> SpecifierSet:
|
||||||
"""Return a SpecifierSet which is a combination of the two sets.
|
"""Return a SpecifierSet which is a combination of the two sets.
|
||||||
|
|
||||||
:param other: The other object to combine with.
|
:param other: The other object to combine with.
|
||||||
|
@ -883,8 +875,8 @@ class SpecifierSet(BaseSpecifier):
|
||||||
def contains(
|
def contains(
|
||||||
self,
|
self,
|
||||||
item: UnparsedVersion,
|
item: UnparsedVersion,
|
||||||
prereleases: Optional[bool] = None,
|
prereleases: bool | None = None,
|
||||||
installed: Optional[bool] = None,
|
installed: bool | None = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Return whether or not the item is contained in this SpecifierSet.
|
"""Return whether or not the item is contained in this SpecifierSet.
|
||||||
|
|
||||||
|
@ -938,7 +930,7 @@ class SpecifierSet(BaseSpecifier):
|
||||||
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
|
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
|
||||||
|
|
||||||
def filter(
|
def filter(
|
||||||
self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
|
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
|
||||||
) -> Iterator[UnparsedVersionVar]:
|
) -> Iterator[UnparsedVersionVar]:
|
||||||
"""Filter items in the given iterable, that match the specifiers in this set.
|
"""Filter items in the given iterable, that match the specifiers in this set.
|
||||||
|
|
||||||
|
@ -995,8 +987,8 @@ class SpecifierSet(BaseSpecifier):
|
||||||
# which will filter out any pre-releases, unless there are no final
|
# which will filter out any pre-releases, unless there are no final
|
||||||
# releases.
|
# releases.
|
||||||
else:
|
else:
|
||||||
filtered: List[UnparsedVersionVar] = []
|
filtered: list[UnparsedVersionVar] = []
|
||||||
found_prereleases: List[UnparsedVersionVar] = []
|
found_prereleases: list[UnparsedVersionVar] = []
|
||||||
|
|
||||||
for item in iterable:
|
for item in iterable:
|
||||||
parsed_version = _coerce_version(item)
|
parsed_version = _coerce_version(item)
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||||
# for complete details.
|
# for complete details.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
@ -11,15 +13,10 @@ import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
from importlib.machinery import EXTENSION_SUFFIXES
|
from importlib.machinery import EXTENSION_SUFFIXES
|
||||||
from typing import (
|
from typing import (
|
||||||
Dict,
|
|
||||||
FrozenSet,
|
|
||||||
Iterable,
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
List,
|
|
||||||
Optional,
|
|
||||||
Sequence,
|
Sequence,
|
||||||
Tuple,
|
Tuple,
|
||||||
Union,
|
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +27,7 @@ logger = logging.getLogger(__name__)
|
||||||
PythonVersion = Sequence[int]
|
PythonVersion = Sequence[int]
|
||||||
MacVersion = Tuple[int, int]
|
MacVersion = Tuple[int, int]
|
||||||
|
|
||||||
INTERPRETER_SHORT_NAMES: Dict[str, str] = {
|
INTERPRETER_SHORT_NAMES: dict[str, str] = {
|
||||||
"python": "py", # Generic.
|
"python": "py", # Generic.
|
||||||
"cpython": "cp",
|
"cpython": "cp",
|
||||||
"pypy": "pp",
|
"pypy": "pp",
|
||||||
|
@ -96,7 +93,7 @@ class Tag:
|
||||||
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.
|
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
|
||||||
|
|
||||||
|
@ -112,8 +109,8 @@ def parse_tag(tag: str) -> FrozenSet[Tag]:
|
||||||
return frozenset(tags)
|
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: Union[int, str, None] = sysconfig.get_config_var(name)
|
value: int | str | None = sysconfig.get_config_var(name)
|
||||||
if value is None and warn:
|
if value is None and warn:
|
||||||
logger.debug(
|
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
|
||||||
|
@ -125,7 +122,7 @@ def _normalize_string(string: str) -> str:
|
||||||
return string.replace(".", "_").replace("-", "_").replace(" ", "_")
|
return string.replace(".", "_").replace("-", "_").replace(" ", "_")
|
||||||
|
|
||||||
|
|
||||||
def _is_threaded_cpython(abis: List[str]) -> bool:
|
def _is_threaded_cpython(abis: list[str]) -> bool:
|
||||||
"""
|
"""
|
||||||
Determine if the ABI corresponds to a threaded (`--disable-gil`) build.
|
Determine if the ABI corresponds to a threaded (`--disable-gil`) build.
|
||||||
|
|
||||||
|
@ -151,7 +148,7 @@ def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool:
|
||||||
return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading
|
return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading
|
||||||
|
|
||||||
|
|
||||||
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.
|
py_version = tuple(py_version) # To allow for version comparison.
|
||||||
abis = []
|
abis = []
|
||||||
version = _version_nodot(py_version[:2])
|
version = _version_nodot(py_version[:2])
|
||||||
|
@ -185,9 +182,9 @@ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
|
||||||
|
|
||||||
|
|
||||||
def cpython_tags(
|
def cpython_tags(
|
||||||
python_version: Optional[PythonVersion] = None,
|
python_version: PythonVersion | None = None,
|
||||||
abis: Optional[Iterable[str]] = None,
|
abis: Iterable[str] | None = None,
|
||||||
platforms: Optional[Iterable[str]] = None,
|
platforms: Iterable[str] | None = None,
|
||||||
*,
|
*,
|
||||||
warn: bool = False,
|
warn: bool = False,
|
||||||
) -> Iterator[Tag]:
|
) -> Iterator[Tag]:
|
||||||
|
@ -244,7 +241,7 @@ def cpython_tags(
|
||||||
yield Tag(interpreter, "abi3", platform_)
|
yield Tag(interpreter, "abi3", platform_)
|
||||||
|
|
||||||
|
|
||||||
def _generic_abi() -> List[str]:
|
def _generic_abi() -> list[str]:
|
||||||
"""
|
"""
|
||||||
Return the ABI tag based on EXT_SUFFIX.
|
Return the ABI tag based on EXT_SUFFIX.
|
||||||
"""
|
"""
|
||||||
|
@ -286,9 +283,9 @@ def _generic_abi() -> List[str]:
|
||||||
|
|
||||||
|
|
||||||
def generic_tags(
|
def generic_tags(
|
||||||
interpreter: Optional[str] = None,
|
interpreter: str | None = None,
|
||||||
abis: Optional[Iterable[str]] = None,
|
abis: Iterable[str] | None = None,
|
||||||
platforms: Optional[Iterable[str]] = None,
|
platforms: Iterable[str] | None = None,
|
||||||
*,
|
*,
|
||||||
warn: bool = False,
|
warn: bool = False,
|
||||||
) -> Iterator[Tag]:
|
) -> Iterator[Tag]:
|
||||||
|
@ -332,9 +329,9 @@ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
|
||||||
|
|
||||||
|
|
||||||
def compatible_tags(
|
def compatible_tags(
|
||||||
python_version: Optional[PythonVersion] = None,
|
python_version: PythonVersion | None = None,
|
||||||
interpreter: Optional[str] = None,
|
interpreter: str | None = None,
|
||||||
platforms: Optional[Iterable[str]] = None,
|
platforms: Iterable[str] | None = None,
|
||||||
) -> Iterator[Tag]:
|
) -> Iterator[Tag]:
|
||||||
"""
|
"""
|
||||||
Yields the sequence of tags that are compatible with a specific version of Python.
|
Yields the sequence of tags that are compatible with a specific version of Python.
|
||||||
|
@ -366,7 +363,7 @@ def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
|
||||||
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]
|
formats = [cpu_arch]
|
||||||
if cpu_arch == "x86_64":
|
if cpu_arch == "x86_64":
|
||||||
if version < (10, 4):
|
if version < (10, 4):
|
||||||
|
@ -399,7 +396,7 @@ def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
|
||||||
|
|
||||||
|
|
||||||
def mac_platforms(
|
def mac_platforms(
|
||||||
version: Optional[MacVersion] = None, arch: Optional[str] = None
|
version: MacVersion | None = None, arch: str | None = None
|
||||||
) -> Iterator[str]:
|
) -> Iterator[str]:
|
||||||
"""
|
"""
|
||||||
Yields the platform tags for a macOS system.
|
Yields the platform tags for a macOS system.
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||||
# for complete details.
|
# for complete details.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import FrozenSet, NewType, Tuple, Union, cast
|
from typing import NewType, Tuple, Union, cast
|
||||||
|
|
||||||
from .tags import Tag, parse_tag
|
from .tags import Tag, parse_tag
|
||||||
from .version import InvalidVersion, Version
|
from .version import InvalidVersion, Version
|
||||||
|
@ -53,7 +55,7 @@ def is_normalized_name(name: str) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def canonicalize_version(
|
def canonicalize_version(
|
||||||
version: Union[Version, str], *, strip_trailing_zero: bool = True
|
version: Version | str, *, strip_trailing_zero: bool = True
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
This is very similar to Version.__str__, but has one subtle difference
|
This is very similar to Version.__str__, but has one subtle difference
|
||||||
|
@ -102,7 +104,7 @@ def canonicalize_version(
|
||||||
|
|
||||||
def parse_wheel_filename(
|
def parse_wheel_filename(
|
||||||
filename: str,
|
filename: str,
|
||||||
) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
|
) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
|
||||||
if not filename.endswith(".whl"):
|
if not filename.endswith(".whl"):
|
||||||
raise InvalidWheelFilename(
|
raise InvalidWheelFilename(
|
||||||
f"Invalid wheel filename (extension must be '.whl'): {filename}"
|
f"Invalid wheel filename (extension must be '.whl'): {filename}"
|
||||||
|
@ -143,7 +145,7 @@ def parse_wheel_filename(
|
||||||
return (name, version, build, tags)
|
return (name, version, build, tags)
|
||||||
|
|
||||||
|
|
||||||
def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]:
|
def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
|
||||||
if filename.endswith(".tar.gz"):
|
if filename.endswith(".tar.gz"):
|
||||||
file_stem = filename[: -len(".tar.gz")]
|
file_stem = filename[: -len(".tar.gz")]
|
||||||
elif filename.endswith(".zip"):
|
elif filename.endswith(".zip"):
|
||||||
|
|
|
@ -7,9 +7,11 @@
|
||||||
from packaging.version import parse, Version
|
from packaging.version import parse, Version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
from typing import Any, Callable, NamedTuple, Optional, SupportsInt, Tuple, Union
|
from typing import Any, Callable, NamedTuple, SupportsInt, Tuple, Union
|
||||||
|
|
||||||
from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
|
from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
|
||||||
|
|
||||||
|
@ -35,14 +37,14 @@ VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool]
|
||||||
|
|
||||||
class _Version(NamedTuple):
|
class _Version(NamedTuple):
|
||||||
epoch: int
|
epoch: int
|
||||||
release: Tuple[int, ...]
|
release: tuple[int, ...]
|
||||||
dev: Optional[Tuple[str, int]]
|
dev: tuple[str, int] | None
|
||||||
pre: Optional[Tuple[str, int]]
|
pre: tuple[str, int] | None
|
||||||
post: Optional[Tuple[str, int]]
|
post: tuple[str, int] | None
|
||||||
local: Optional[LocalType]
|
local: LocalType | None
|
||||||
|
|
||||||
|
|
||||||
def parse(version: str) -> "Version":
|
def parse(version: str) -> Version:
|
||||||
"""Parse the given version string.
|
"""Parse the given version string.
|
||||||
|
|
||||||
>>> parse('1.0.dev1')
|
>>> parse('1.0.dev1')
|
||||||
|
@ -65,7 +67,7 @@ class InvalidVersion(ValueError):
|
||||||
|
|
||||||
|
|
||||||
class _BaseVersion:
|
class _BaseVersion:
|
||||||
_key: Tuple[Any, ...]
|
_key: tuple[Any, ...]
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(self._key)
|
return hash(self._key)
|
||||||
|
@ -73,13 +75,13 @@ class _BaseVersion:
|
||||||
# Please keep the duplicated `isinstance` check
|
# Please keep the duplicated `isinstance` check
|
||||||
# in the six comparisons hereunder
|
# in the six comparisons hereunder
|
||||||
# unless you find a way to avoid adding overhead function calls.
|
# 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):
|
if not isinstance(other, _BaseVersion):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
return self._key < other._key
|
return self._key < other._key
|
||||||
|
|
||||||
def __le__(self, other: "_BaseVersion") -> bool:
|
def __le__(self, other: _BaseVersion) -> bool:
|
||||||
if not isinstance(other, _BaseVersion):
|
if not isinstance(other, _BaseVersion):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
@ -91,13 +93,13 @@ class _BaseVersion:
|
||||||
|
|
||||||
return self._key == other._key
|
return self._key == other._key
|
||||||
|
|
||||||
def __ge__(self, other: "_BaseVersion") -> bool:
|
def __ge__(self, other: _BaseVersion) -> bool:
|
||||||
if not isinstance(other, _BaseVersion):
|
if not isinstance(other, _BaseVersion):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
return self._key >= other._key
|
return self._key >= other._key
|
||||||
|
|
||||||
def __gt__(self, other: "_BaseVersion") -> bool:
|
def __gt__(self, other: _BaseVersion) -> bool:
|
||||||
if not isinstance(other, _BaseVersion):
|
if not isinstance(other, _BaseVersion):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
@ -274,7 +276,7 @@ class Version(_BaseVersion):
|
||||||
return self._version.epoch
|
return self._version.epoch
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def release(self) -> Tuple[int, ...]:
|
def release(self) -> tuple[int, ...]:
|
||||||
"""The components of the "release" segment of the version.
|
"""The components of the "release" segment of the version.
|
||||||
|
|
||||||
>>> Version("1.2.3").release
|
>>> Version("1.2.3").release
|
||||||
|
@ -290,7 +292,7 @@ class Version(_BaseVersion):
|
||||||
return self._version.release
|
return self._version.release
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pre(self) -> Optional[Tuple[str, int]]:
|
def pre(self) -> tuple[str, int] | None:
|
||||||
"""The pre-release segment of the version.
|
"""The pre-release segment of the version.
|
||||||
|
|
||||||
>>> print(Version("1.2.3").pre)
|
>>> print(Version("1.2.3").pre)
|
||||||
|
@ -305,7 +307,7 @@ class Version(_BaseVersion):
|
||||||
return self._version.pre
|
return self._version.pre
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def post(self) -> Optional[int]:
|
def post(self) -> int | None:
|
||||||
"""The post-release number of the version.
|
"""The post-release number of the version.
|
||||||
|
|
||||||
>>> print(Version("1.2.3").post)
|
>>> print(Version("1.2.3").post)
|
||||||
|
@ -316,7 +318,7 @@ class Version(_BaseVersion):
|
||||||
return self._version.post[1] if self._version.post else None
|
return self._version.post[1] if self._version.post else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dev(self) -> Optional[int]:
|
def dev(self) -> int | None:
|
||||||
"""The development number of the version.
|
"""The development number of the version.
|
||||||
|
|
||||||
>>> print(Version("1.2.3").dev)
|
>>> print(Version("1.2.3").dev)
|
||||||
|
@ -327,7 +329,7 @@ class Version(_BaseVersion):
|
||||||
return self._version.dev[1] if self._version.dev else None
|
return self._version.dev[1] if self._version.dev else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def local(self) -> Optional[str]:
|
def local(self) -> str | None:
|
||||||
"""The local version segment of the version.
|
"""The local version segment of the version.
|
||||||
|
|
||||||
>>> print(Version("1.2.3").local)
|
>>> print(Version("1.2.3").local)
|
||||||
|
@ -450,9 +452,8 @@ class Version(_BaseVersion):
|
||||||
|
|
||||||
|
|
||||||
def _parse_letter_version(
|
def _parse_letter_version(
|
||||||
letter: Optional[str], number: Union[str, bytes, SupportsInt, None]
|
letter: str | None, number: str | bytes | SupportsInt | None
|
||||||
) -> Optional[Tuple[str, int]]:
|
) -> tuple[str, int] | None:
|
||||||
|
|
||||||
if letter:
|
if letter:
|
||||||
# We consider there to be an implicit 0 in a pre-release if there is
|
# We consider there to be an implicit 0 in a pre-release if there is
|
||||||
# not a numeral associated with it.
|
# not a numeral associated with it.
|
||||||
|
@ -488,7 +489,7 @@ def _parse_letter_version(
|
||||||
_local_version_separators = re.compile(r"[\._-]")
|
_local_version_separators = re.compile(r"[\._-]")
|
||||||
|
|
||||||
|
|
||||||
def _parse_local_version(local: Optional[str]) -> Optional[LocalType]:
|
def _parse_local_version(local: str | None) -> LocalType | None:
|
||||||
"""
|
"""
|
||||||
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
|
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
|
||||||
"""
|
"""
|
||||||
|
@ -502,13 +503,12 @@ def _parse_local_version(local: Optional[str]) -> Optional[LocalType]:
|
||||||
|
|
||||||
def _cmpkey(
|
def _cmpkey(
|
||||||
epoch: int,
|
epoch: int,
|
||||||
release: Tuple[int, ...],
|
release: tuple[int, ...],
|
||||||
pre: Optional[Tuple[str, int]],
|
pre: tuple[str, int] | None,
|
||||||
post: Optional[Tuple[str, int]],
|
post: tuple[str, int] | None,
|
||||||
dev: Optional[Tuple[str, int]],
|
dev: tuple[str, int] | None,
|
||||||
local: Optional[LocalType],
|
local: LocalType | None,
|
||||||
) -> CmpKey:
|
) -> CmpKey:
|
||||||
|
|
||||||
# When we compare a release version, we want to compare it with all of the
|
# When we compare a release version, we want to compare it with all of the
|
||||||
# trailing zeros removed. So we'll use a reverse the list, drop all the now
|
# trailing zeros removed. So we'll use a reverse the list, drop all the now
|
||||||
# leading zeros until we come to something non zero, then take the rest
|
# leading zeros until we come to something non zero, then take the rest
|
||||||
|
|
|
@ -23,7 +23,7 @@ IPy==1.01
|
||||||
Mako==1.3.5
|
Mako==1.3.5
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==2.1.5
|
||||||
musicbrainzngs==0.7.1
|
musicbrainzngs==0.7.1
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
paho-mqtt==2.1.0
|
paho-mqtt==2.1.0
|
||||||
platformdirs==4.2.2
|
platformdirs==4.2.2
|
||||||
plexapi==4.15.13
|
plexapi==4.15.13
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue