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:
dependabot[bot] 2024-06-19 00:04:34 -07:00 committed by GitHub
parent 28ad2716ba
commit 2f1607b96b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 288 additions and 238 deletions

View file

@ -6,7 +6,7 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "24.0"
__version__ = "24.1"
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"

View file

@ -8,10 +8,12 @@ Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
"""
from __future__ import annotations
import enum
import os
import struct
from typing import IO, Optional, Tuple
from typing import IO
class ELFInvalid(ValueError):
@ -87,11 +89,11 @@ class ELFFile:
except struct.error as 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)))
@property
def interpreter(self) -> Optional[str]:
def interpreter(self) -> str | None:
"""
The path recorded in the ``PT_INTERP`` section header.
"""

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import collections
import contextlib
import functools
@ -5,7 +7,7 @@ import os
import re
import sys
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
@ -17,7 +19,7 @@ EF_ARM_ABI_FLOAT_HARD = 0x00000400
# `os.PathLike` not a generic type until Python 3.9, so sticking with `str`
# as the type for `path` until then.
@contextlib.contextmanager
def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]:
def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]:
try:
with open(path, "rb") as 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
# 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):
@ -80,7 +82,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.
"""
@ -90,7 +92,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
# https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
try:
# 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
_, version = version_string.rsplit()
except (AssertionError, AttributeError, OSError, ValueError):
@ -99,7 +101,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.
"""
@ -143,12 +145,12 @@ def _glibc_version_string_ctypes() -> Optional[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."""
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
@ -167,8 +169,8 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
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)

View file

@ -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.
"""
from __future__ import annotations
import functools
import re
import subprocess
import sys
from typing import Iterator, NamedTuple, Optional, Sequence
from typing import Iterator, NamedTuple, Sequence
from ._elffile import ELFFile
@ -18,7 +20,7 @@ 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":
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)))
@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

View file

@ -1,11 +1,13 @@
"""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.
"""
from __future__ import annotations
import ast
from typing import Any, List, NamedTuple, Optional, Tuple, Union
from typing import NamedTuple, Sequence, Tuple, Union
from ._tokenizer import DEFAULT_RULES, Tokenizer
@ -41,20 +43,16 @@ class Op(Node):
MarkerVar = Union[Variable, Value]
MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]]
# MarkerList = List[Union["MarkerList", MarkerAtom, str]]
# mypy does not support recursive type definition
# https://github.com/python/mypy/issues/731
MarkerAtom = Any
MarkerList = List[Any]
MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]]
MarkerList = Sequence[Union["MarkerList", MarkerAtom, str]]
class ParsedRequirement(NamedTuple):
name: str
url: str
extras: List[str]
extras: list[str]
specifier: str
marker: Optional[MarkerList]
marker: MarkerList | None
# --------------------------------------------------------------------------------------
@ -87,7 +85,7 @@ def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement:
def _parse_requirement_details(
tokenizer: Tokenizer,
) -> Tuple[str, str, Optional[MarkerList]]:
) -> tuple[str, str, MarkerList | None]:
"""
requirement_details = AT URL (WS requirement_marker?)?
| specifier WS? (requirement_marker)?
@ -156,7 +154,7 @@ def _parse_requirement_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)?
"""
@ -175,11 +173,11 @@ def _parse_extras(tokenizer: Tokenizer) -> List[str]:
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[str] = []
extras: list[str] = []
if not tokenizer.check("IDENTIFIER"):
return extras

View file

@ -1,7 +1,9 @@
from __future__ import annotations
import contextlib
import re
from dataclasses import dataclass
from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union
from typing import Iterator, NoReturn
from .specifiers import Specifier
@ -21,7 +23,7 @@ class ParserSyntaxError(Exception):
message: str,
*,
source: str,
span: Tuple[int, int],
span: tuple[int, int],
) -> None:
self.span = span
self.message = message
@ -34,7 +36,7 @@ class ParserSyntaxError(Exception):
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"\(",
"RIGHT_PARENTHESIS": r"\)",
"LEFT_BRACKET": r"\[",
@ -96,13 +98,13 @@ class Tokenizer:
self,
source: str,
*,
rules: "Dict[str, Union[str, re.Pattern[str]]]",
rules: dict[str, str | re.Pattern[str]],
) -> None:
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()
}
self.next_token: Optional[Token] = None
self.next_token: Token | None = None
self.position = 0
def consume(self, name: str) -> None:
@ -154,8 +156,8 @@ class Tokenizer:
self,
message: str,
*,
span_start: Optional[int] = None,
span_end: Optional[int] = None,
span_start: int | None = None,
span_end: int | None = None,
) -> NoReturn:
"""Raise ParserSyntaxError at the given position."""
span = (

View file

@ -2,20 +2,16 @@
# 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, Callable, TypedDict, cast
from ._parser import (
MarkerAtom,
MarkerList,
Op,
Value,
Variable,
parse_marker as _parse_marker,
)
from ._parser import MarkerAtom, MarkerList, Op, Value, Variable
from ._parser import parse_marker as _parse_marker
from ._tokenizer import ParserSyntaxError
from .specifiers import InvalidSpecifier, Specifier
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:
"""
Normalize extra values.
@ -67,9 +135,8 @@ def _normalize_extra_values(results: Any) -> Any:
def _format_marker(
marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True
marker: list[str] | MarkerAtom | str, first: bool | None = True
) -> str:
assert isinstance(marker, (list, tuple, str))
# Sometimes we have a structure like [[...]] which is a single item list
@ -95,7 +162,7 @@ def _format_marker(
return marker
_operators: Dict[str, Operator] = {
_operators: dict[str, Operator] = {
"in": lambda lhs, rhs: lhs in rhs,
"not in": lambda lhs, rhs: lhs not in rhs,
"<": operator.lt,
@ -115,14 +182,14 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
else:
return spec.contains(lhs, prereleases=True)
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}.")
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
# https://peps.python.org/pep-0685/
# > When comparing extra names, tools MUST normalize the names being
@ -134,8 +201,8 @@ def _normalize(*values: str, key: str) -> Tuple[str, ...]:
return values
def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
groups: List[List[bool]] = [[]]
def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool:
groups: list[list[bool]] = [[]]
for marker in markers:
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)
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)
kind = info.releaselevel
if kind != "final":
@ -172,7 +239,7 @@ def format_full_version(info: "sys._version_info") -> str:
return version
def default_environment() -> Dict[str, str]:
def default_environment() -> Environment:
iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name
return {
@ -231,7 +298,7 @@ class Marker:
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.
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.
"""
current_environment = default_environment()
current_environment = cast("dict[str, str]", default_environment())
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:
current_environment.update(environment)
# The API used to allow setting extra to None. We need to handle this

View file

@ -1,50 +1,31 @@
from __future__ import annotations
import email.feedparser
import email.header
import email.message
import email.parser
import email.policy
import sys
import typing
from typing import (
Any,
Callable,
Dict,
Generic,
List,
Optional,
Tuple,
Type,
Union,
Literal,
TypedDict,
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")
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:
ExceptionGroup
except NameError: # pragma: no cover
class ExceptionGroup(Exception): # noqa: N818
class ExceptionGroup(Exception):
"""A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
If :external:exc:`ExceptionGroup` is already defined by Python itself,
@ -52,9 +33,9 @@ except NameError: # pragma: no cover
"""
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.exceptions = exceptions
@ -100,32 +81,32 @@ class RawMetadata(TypedDict, total=False):
metadata_version: str
name: str
version: str
platforms: List[str]
platforms: list[str]
summary: str
description: str
keywords: List[str]
keywords: list[str]
home_page: str
author: str
author_email: str
license: str
# Metadata 1.1 - PEP 314
supported_platforms: List[str]
supported_platforms: list[str]
download_url: str
classifiers: List[str]
requires: List[str]
provides: List[str]
obsoletes: List[str]
classifiers: list[str]
requires: list[str]
provides: list[str]
obsoletes: list[str]
# Metadata 1.2 - PEP 345
maintainer: str
maintainer_email: str
requires_dist: List[str]
provides_dist: List[str]
obsoletes_dist: List[str]
requires_dist: list[str]
provides_dist: list[str]
obsoletes_dist: list[str]
requires_python: str
requires_external: List[str]
project_urls: Dict[str, str]
requires_external: list[str]
project_urls: dict[str, str]
# Metadata 2.0
# PEP 426 attempted to completely revamp the metadata format
@ -138,10 +119,10 @@ class RawMetadata(TypedDict, total=False):
# Metadata 2.1 - PEP 566
description_content_type: str
provides_extra: List[str]
provides_extra: list[str]
# Metadata 2.2 - PEP 643
dynamic: List[str]
dynamic: list[str]
# Metadata 2.3 - PEP 685
# 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."""
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."""
urls = {}
for pair in data:
@ -230,7 +211,7 @@ def _parse_project_urls(data: List[str]) -> Dict[str, str]:
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."""
# If our source is a str, then our caller has managed encodings for us,
# 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()}
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``).
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.
"""
raw: Dict[str, Union[str, List[str], Dict[str, str]]] = {}
unparsed: Dict[str, List[str]] = {}
raw: dict[str, str | list[str] | dict[str, str]] = {}
unparsed: dict[str, list[str]] = {}
if isinstance(data, str):
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
# can be independently encoded, so we'll need to check each
# of them.
chunks: List[Tuple[bytes, Optional[str]]] = []
chunks: list[tuple[bytes, str | None]] = []
for bin, encoding in email.header.decode_header(h):
try:
bin.decode("utf8", "strict")
@ -499,11 +480,11 @@ class _Validator(Generic[T]):
) -> None:
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.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().
# No need to check the cache as attribute lookup will resolve into the
# instance's __dict__ before __get__ is called.
@ -531,7 +512,7 @@ class _Validator(Generic[T]):
return cast(T, value)
def _invalid_metadata(
self, msg: str, cause: Optional[Exception] = None
self, msg: str, cause: Exception | None = None
) -> InvalidMetadata:
exc = InvalidMetadata(
self.raw_name, msg.format_map({"field": repr(self.raw_name)})
@ -606,7 +587,7 @@ class _Validator(Generic[T]):
)
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):
if dynamic_field in {"name", "version", "metadata-version"}:
raise self._invalid_metadata(
@ -618,8 +599,8 @@ class _Validator(Generic[T]):
def _process_provides_extra(
self,
value: List[str],
) -> List[utils.NormalizedName]:
value: list[str],
) -> list[utils.NormalizedName]:
normalized_names = []
try:
for name in value:
@ -641,8 +622,8 @@ class _Validator(Generic[T]):
def _process_requires_dist(
self,
value: List[str],
) -> List[requirements.Requirement]:
value: list[str],
) -> list[requirements.Requirement]:
reqs = []
try:
for req in value:
@ -665,7 +646,7 @@ class Metadata:
_raw: RawMetadata
@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`.
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.
if validate:
exceptions: List[Exception] = []
exceptions: list[Exception] = []
try:
metadata_version = ins.metadata_version
metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version)
@ -722,9 +703,7 @@ class Metadata:
return ins
@classmethod
def from_email(
cls, data: Union[bytes, str], *, validate: bool = True
) -> "Metadata":
def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
"""Parse metadata from email headers.
If *validate* is true, the metadata will be validated. All exceptions
@ -760,66 +739,66 @@ class Metadata:
*validate* parameter)"""
version: _Validator[version_module.Version] = _Validator()
""":external:ref:`core-metadata-version` (required)"""
dynamic: _Validator[Optional[List[str]]] = _Validator(
dynamic: _Validator[list[str] | None] = _Validator(
added="2.2",
)
""":external:ref:`core-metadata-dynamic`
(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`"""
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`"""
summary: _Validator[Optional[str]] = _Validator()
summary: _Validator[str | None] = _Validator()
""":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`"""
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)"""
keywords: _Validator[Optional[List[str]]] = _Validator()
keywords: _Validator[list[str] | None] = _Validator()
""":external:ref:`core-metadata-keywords`"""
home_page: _Validator[Optional[str]] = _Validator()
home_page: _Validator[str | None] = _Validator()
""":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`"""
author: _Validator[Optional[str]] = _Validator()
author: _Validator[str | None] = _Validator()
""":external:ref:`core-metadata-author`"""
author_email: _Validator[Optional[str]] = _Validator()
author_email: _Validator[str | None] = _Validator()
""":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`"""
maintainer_email: _Validator[Optional[str]] = _Validator(added="1.2")
maintainer_email: _Validator[str | None] = _Validator(added="1.2")
""":external:ref:`core-metadata-maintainer-email`"""
license: _Validator[Optional[str]] = _Validator()
license: _Validator[str | None] = _Validator()
""":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`"""
requires_dist: _Validator[Optional[List[requirements.Requirement]]] = _Validator(
requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(
added="1.2"
)
""":external:ref:`core-metadata-requires-dist`"""
requires_python: _Validator[Optional[specifiers.SpecifierSet]] = _Validator(
requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator(
added="1.2"
)
""":external:ref:`core-metadata-requires-python`"""
# Because `Requires-External` allows for non-PEP 440 version specifiers, we
# 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`"""
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`"""
# PEP 685 lets us raise an error if an extra doesn't pass `Name` validation
# regardless of metadata version.
provides_extra: _Validator[Optional[List[utils.NormalizedName]]] = _Validator(
provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator(
added="2.1",
)
""":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`"""
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`"""
requires: _Validator[Optional[List[str]]] = _Validator(added="1.1")
requires: _Validator[list[str] | None] = _Validator(added="1.1")
"""``Requires`` (deprecated)"""
provides: _Validator[Optional[List[str]]] = _Validator(added="1.1")
provides: _Validator[list[str] | None] = _Validator(added="1.1")
"""``Provides`` (deprecated)"""
obsoletes: _Validator[Optional[List[str]]] = _Validator(added="1.1")
obsoletes: _Validator[list[str] | None] = _Validator(added="1.1")
"""``Obsoletes`` (deprecated)"""

View file

@ -1,8 +1,9 @@
# 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 typing import Any, Iterator, Optional, Set
from typing import Any, Iterator
from ._parser import parse_requirement as _parse_requirement
from ._tokenizer import ParserSyntaxError
@ -37,10 +38,10 @@ class Requirement:
raise InvalidRequirement(str(e)) from e
self.name: str = parsed.name
self.url: Optional[str] = parsed.url or None
self.extras: Set[str] = set(parsed.extras or [])
self.url: str | None = parsed.url or None
self.extras: set[str] = set(parsed.extras or [])
self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
self.marker: Optional[Marker] = None
self.marker: Marker | None = None
if parsed.marker is not None:
self.marker = Marker.__new__(Marker)
self.marker._markers = _normalize_extra_values(parsed.marker)

View file

@ -8,10 +8,12 @@
from packaging.version import Version
"""
from __future__ import annotations
import abc
import itertools
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 .version import Version
@ -64,7 +66,7 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def prereleases(self) -> Optional[bool]:
def prereleases(self) -> bool | None:
"""Whether or not pre-releases as a whole are allowed.
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
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[UnparsedVersionVar], prereleases: Optional[bool] = None
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
) -> Iterator[UnparsedVersionVar]:
"""
Takes an iterable of items and filters them so that only items which
@ -217,7 +219,7 @@ class Specifier(BaseSpecifier):
"===": "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.
:param spec:
@ -234,7 +236,7 @@ class Specifier(BaseSpecifier):
if not match:
raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
self._spec: Tuple[str, str] = (
self._spec: tuple[str, str] = (
match.group("operator").strip(),
match.group("version").strip(),
)
@ -318,7 +320,7 @@ class Specifier(BaseSpecifier):
return "{}{}".format(*self._spec)
@property
def _canonical_spec(self) -> Tuple[str, str]:
def _canonical_spec(self) -> tuple[str, str]:
canonical_version = canonicalize_version(
self._spec[1],
strip_trailing_zero=(self._spec[0] != "~="),
@ -364,7 +366,6 @@ class Specifier(BaseSpecifier):
return operator_callable
def _compare_compatible(self, prospective: Version, spec: str) -> bool:
# Compatible releases have an equivalent combination of >= and ==. That
# is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
# 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:
# We need special logic to handle prefix matching
if spec.endswith(".*"):
# 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)
def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
# the prospective version.
return Version(prospective.public) <= Version(spec)
def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
# the prospective version.
return Version(prospective.public) >= Version(spec)
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
# it as a version.
spec = Version(spec_str)
@ -468,7 +465,6 @@ class Specifier(BaseSpecifier):
return True
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
# it as a version.
spec = Version(spec_str)
@ -501,7 +497,7 @@ class Specifier(BaseSpecifier):
def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
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.
:param item: The item to check for.
@ -522,9 +518,7 @@ class Specifier(BaseSpecifier):
"""
return self.contains(item)
def contains(
self, item: UnparsedVersion, prereleases: Optional[bool] = None
) -> bool:
def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:
"""Return whether or not the item is contained in this specifier.
:param item:
@ -569,7 +563,7 @@ class Specifier(BaseSpecifier):
return operator_callable(normalized_item, self.version)
def filter(
self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
) -> Iterator[UnparsedVersionVar]:
"""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]+)$")
def _version_split(version: str) -> List[str]:
def _version_split(version: str) -> list[str]:
"""Split version into components.
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
version string.
"""
result: List[str] = []
result: list[str] = []
epoch, _, rest = version.rpartition("!")
result.append(epoch or "0")
@ -655,7 +649,7 @@ def _version_split(version: str) -> List[str]:
return result
def _version_join(components: List[str]) -> str:
def _version_join(components: list[str]) -> str:
"""Join split version components into a version string.
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 = [], []
# Get the release segment of our versions
@ -700,9 +694,7 @@ class SpecifierSet(BaseSpecifier):
specifiers (``>=3.0,!=3.1``), or no specifier at all.
"""
def __init__(
self, specifiers: str = "", prereleases: Optional[bool] = None
) -> None:
def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> None:
"""Initialize a SpecifierSet instance.
:param specifiers:
@ -730,7 +722,7 @@ class SpecifierSet(BaseSpecifier):
self._prereleases = prereleases
@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.
if self._prereleases is not None:
@ -787,7 +779,7 @@ class SpecifierSet(BaseSpecifier):
def __hash__(self) -> int:
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.
:param other: The other object to combine with.
@ -883,8 +875,8 @@ class SpecifierSet(BaseSpecifier):
def contains(
self,
item: UnparsedVersion,
prereleases: Optional[bool] = None,
installed: Optional[bool] = None,
prereleases: bool | None = None,
installed: bool | None = None,
) -> bool:
"""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)
def filter(
self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
) -> Iterator[UnparsedVersionVar]:
"""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
# releases.
else:
filtered: List[UnparsedVersionVar] = []
found_prereleases: List[UnparsedVersionVar] = []
filtered: list[UnparsedVersionVar] = []
found_prereleases: list[UnparsedVersionVar] = []
for item in iterable:
parsed_version = _coerce_version(item)

View file

@ -2,6 +2,8 @@
# 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 re
@ -11,15 +13,10 @@ import sys
import sysconfig
from importlib.machinery import EXTENSION_SUFFIXES
from typing import (
Dict,
FrozenSet,
Iterable,
Iterator,
List,
Optional,
Sequence,
Tuple,
Union,
cast,
)
@ -30,7 +27,7 @@ logger = logging.getLogger(__name__)
PythonVersion = Sequence[int]
MacVersion = Tuple[int, int]
INTERPRETER_SHORT_NAMES: Dict[str, str] = {
INTERPRETER_SHORT_NAMES: dict[str, str] = {
"python": "py", # Generic.
"cpython": "cp",
"pypy": "pp",
@ -96,7 +93,7 @@ class Tag:
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.
@ -112,8 +109,8 @@ def parse_tag(tag: str) -> FrozenSet[Tag]:
return frozenset(tags)
def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]:
value: Union[int, str, None] = sysconfig.get_config_var(name)
def _get_config_var(name: str, warn: bool = False) -> int | str | None:
value: int | str | None = 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
@ -125,7 +122,7 @@ def _normalize_string(string: str) -> str:
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.
@ -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
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])
@ -185,9 +182,9 @@ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
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]:
@ -244,7 +241,7 @@ def cpython_tags(
yield Tag(interpreter, "abi3", platform_)
def _generic_abi() -> List[str]:
def _generic_abi() -> list[str]:
"""
Return the ABI tag based on EXT_SUFFIX.
"""
@ -286,9 +283,9 @@ def _generic_abi() -> List[str]:
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]:
@ -332,9 +329,9 @@ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
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.
@ -366,7 +363,7 @@ def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
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 version < (10, 4):
@ -399,7 +396,7 @@ def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
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.

View file

@ -2,8 +2,10 @@
# 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 NewType, Tuple, Union, cast
from .tags import Tag, parse_tag
from .version import InvalidVersion, Version
@ -53,7 +55,7 @@ def is_normalized_name(name: str) -> bool:
def canonicalize_version(
version: Union[Version, str], *, strip_trailing_zero: bool = True
version: Version | str, *, strip_trailing_zero: bool = True
) -> str:
"""
This is very similar to Version.__str__, but has one subtle difference
@ -102,7 +104,7 @@ def canonicalize_version(
def parse_wheel_filename(
filename: str,
) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
if not filename.endswith(".whl"):
raise InvalidWheelFilename(
f"Invalid wheel filename (extension must be '.whl'): {filename}"
@ -143,7 +145,7 @@ def parse_wheel_filename(
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"):
file_stem = filename[: -len(".tar.gz")]
elif filename.endswith(".zip"):

View file

@ -7,9 +7,11 @@
from packaging.version import parse, Version
"""
from __future__ import annotations
import itertools
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
@ -35,14 +37,14 @@ VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool]
class _Version(NamedTuple):
epoch: int
release: Tuple[int, ...]
dev: Optional[Tuple[str, int]]
pre: Optional[Tuple[str, int]]
post: Optional[Tuple[str, int]]
local: Optional[LocalType]
release: tuple[int, ...]
dev: tuple[str, int] | None
pre: tuple[str, int] | None
post: tuple[str, int] | None
local: LocalType | None
def parse(version: str) -> "Version":
def parse(version: str) -> Version:
"""Parse the given version string.
>>> parse('1.0.dev1')
@ -65,7 +67,7 @@ class InvalidVersion(ValueError):
class _BaseVersion:
_key: Tuple[Any, ...]
_key: tuple[Any, ...]
def __hash__(self) -> int:
return hash(self._key)
@ -73,13 +75,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
@ -91,13 +93,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
@ -274,7 +276,7 @@ class Version(_BaseVersion):
return self._version.epoch
@property
def release(self) -> Tuple[int, ...]:
def release(self) -> tuple[int, ...]:
"""The components of the "release" segment of the version.
>>> Version("1.2.3").release
@ -290,7 +292,7 @@ class Version(_BaseVersion):
return self._version.release
@property
def pre(self) -> Optional[Tuple[str, int]]:
def pre(self) -> tuple[str, int] | None:
"""The pre-release segment of the version.
>>> print(Version("1.2.3").pre)
@ -305,7 +307,7 @@ class Version(_BaseVersion):
return self._version.pre
@property
def post(self) -> Optional[int]:
def post(self) -> int | None:
"""The post-release number of the version.
>>> print(Version("1.2.3").post)
@ -316,7 +318,7 @@ class Version(_BaseVersion):
return self._version.post[1] if self._version.post else None
@property
def dev(self) -> Optional[int]:
def dev(self) -> int | None:
"""The development number of the version.
>>> print(Version("1.2.3").dev)
@ -327,7 +329,7 @@ class Version(_BaseVersion):
return self._version.dev[1] if self._version.dev else None
@property
def local(self) -> Optional[str]:
def local(self) -> str | None:
"""The local version segment of the version.
>>> print(Version("1.2.3").local)
@ -450,9 +452,8 @@ class Version(_BaseVersion):
def _parse_letter_version(
letter: Optional[str], number: Union[str, bytes, SupportsInt, None]
) -> Optional[Tuple[str, int]]:
letter: str | None, number: str | bytes | SupportsInt | None
) -> tuple[str, int] | None:
if letter:
# We consider there to be an implicit 0 in a pre-release if there is
# not a numeral associated with it.
@ -488,7 +489,7 @@ def _parse_letter_version(
_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").
"""
@ -502,13 +503,12 @@ def _parse_local_version(local: Optional[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[LocalType],
release: tuple[int, ...],
pre: tuple[str, int] | None,
post: tuple[str, int] | None,
dev: tuple[str, int] | None,
local: LocalType | None,
) -> CmpKey:
# 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
# leading zeros until we come to something non zero, then take the rest

View file

@ -23,7 +23,7 @@ IPy==1.01
Mako==1.3.5
MarkupSafe==2.1.5
musicbrainzngs==0.7.1
packaging==24.0
packaging==24.1
paho-mqtt==2.1.0
platformdirs==4.2.2
plexapi==4.15.13