diff options
| author | robot-piglet <[email protected]> | 2025-05-12 10:40:00 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2025-05-12 10:50:44 +0300 |
| commit | 3828d7ccd94bdcdb1515d956d96f5050b158f3f4 (patch) | |
| tree | 7090aafadb84c91a6bef3213bf562efe1256401b /contrib/python | |
| parent | c9bc4600b34d872d3c33a9a6b4164b1fbd0cca88 (diff) | |
Intermediate changes
commit_hash:c5767cc2625ee5d7a34bd8cade5a63abc114ae18
Diffstat (limited to 'contrib/python')
8 files changed, 124 insertions, 58 deletions
diff --git a/contrib/python/importlib-metadata/py3/.dist-info/METADATA b/contrib/python/importlib-metadata/py3/.dist-info/METADATA index b5a602cbff7..3df5c095d7b 100644 --- a/contrib/python/importlib-metadata/py3/.dist-info/METADATA +++ b/contrib/python/importlib-metadata/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: importlib_metadata -Version: 8.6.1 +Version: 8.7.0 Summary: Read metadata from Python packages Author-email: "Jason R. Coombs" <[email protected]> Project-URL: Source, https://github.com/python/importlib_metadata @@ -39,6 +39,7 @@ Provides-Extra: enabler Requires-Dist: pytest-enabler>=2.2; extra == "enabler" Provides-Extra: type Requires-Dist: pytest-mypy; extra == "type" +Dynamic: license-file .. image:: https://img.shields.io/pypi/v/importlib_metadata.svg :target: https://pypi.org/project/importlib_metadata @@ -56,7 +57,7 @@ Requires-Dist: pytest-mypy; extra == "type" .. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. image:: https://img.shields.io/badge/skeleton-2025-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/importlib-metadata diff --git a/contrib/python/importlib-metadata/py3/README.rst b/contrib/python/importlib-metadata/py3/README.rst index ffb63387999..a9e634f2247 100644 --- a/contrib/python/importlib-metadata/py3/README.rst +++ b/contrib/python/importlib-metadata/py3/README.rst @@ -14,7 +14,7 @@ .. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. image:: https://img.shields.io/badge/skeleton-2025-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/importlib-metadata diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py b/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py index 0d20b12eae0..deb057b5d50 100644 --- a/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py @@ -22,11 +22,12 @@ import re import sys import textwrap import types +from collections.abc import Iterable, Mapping from contextlib import suppress from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast +from typing import Any from . import _meta from ._collections import FreezableDefaultDict, Pair @@ -37,6 +38,7 @@ from ._compat import ( from ._functools import method_cache, pass_none from ._itertools import always_iterable, bucket, unique_everseen from ._meta import PackageMetadata, SimplePath +from ._typing import md_none from .compat import py39, py311 __all__ = [ @@ -139,6 +141,12 @@ class Sectioned: return line and not line.startswith('#') +class _EntryPointMatch(types.SimpleNamespace): + module: str + attr: str + extras: str + + class EntryPoint: """An entry point as defined by Python packaging conventions. @@ -154,6 +162,30 @@ class EntryPoint: 'attr' >>> ep.extras ['extra1', 'extra2'] + + If the value package or module are not valid identifiers, a + ValueError is raised on access. + + >>> EntryPoint(name=None, group=None, value='invalid-name').module + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + >>> EntryPoint(name=None, group=None, value='invalid-name').attr + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + >>> EntryPoint(name=None, group=None, value='invalid-name').extras + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + + The same thing happens on construction. + + >>> EntryPoint(name=None, group=None, value='invalid-name') + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + """ pattern = re.compile( @@ -181,38 +213,44 @@ class EntryPoint: value: str group: str - dist: Optional[Distribution] = None + dist: Distribution | None = None def __init__(self, name: str, value: str, group: str) -> None: vars(self).update(name=name, value=value, group=group) + self.module def load(self) -> Any: """Load the entry point from its definition. If only a module is indicated by the value, return that module. Otherwise, return the named object. """ - match = cast(Match, self.pattern.match(self.value)) - module = import_module(match.group('module')) - attrs = filter(None, (match.group('attr') or '').split('.')) + module = import_module(self.module) + attrs = filter(None, (self.attr or '').split('.')) return functools.reduce(getattr, attrs, module) @property def module(self) -> str: - match = self.pattern.match(self.value) - assert match is not None - return match.group('module') + return self._match.module @property def attr(self) -> str: - match = self.pattern.match(self.value) - assert match is not None - return match.group('attr') + return self._match.attr @property - def extras(self) -> List[str]: + def extras(self) -> list[str]: + return re.findall(r'\w+', self._match.extras or '') + + @functools.cached_property + def _match(self) -> _EntryPointMatch: match = self.pattern.match(self.value) - assert match is not None - return re.findall(r'\w+', match.group('extras') or '') + if not match: + raise ValueError( + 'Invalid object reference. ' + 'See https://packaging.python.org' + '/en/latest/specifications/entry-points/#data-model', + self.value, + ) + return _EntryPointMatch(**match.groupdict()) def _for(self, dist): vars(self).update(dist=dist) @@ -311,14 +349,14 @@ class EntryPoints(tuple): return EntryPoints(ep for ep in self if py39.ep_matches(ep, **params)) @property - def names(self) -> Set[str]: + def names(self) -> set[str]: """ Return the set of all names of all entry points. """ return {ep.name for ep in self} @property - def groups(self) -> Set[str]: + def groups(self) -> set[str]: """ Return the set of all groups of all entry points. """ @@ -339,7 +377,7 @@ class EntryPoints(tuple): class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" - hash: Optional[FileHash] + hash: FileHash | None size: int dist: Distribution @@ -374,7 +412,7 @@ class Distribution(metaclass=abc.ABCMeta): """ @abc.abstractmethod - def read_text(self, filename) -> Optional[str]: + def read_text(self, filename) -> str | None: """Attempt to load metadata file given by the name. Python distribution metadata is organized by blobs of text @@ -434,7 +472,7 @@ class Distribution(metaclass=abc.ABCMeta): @classmethod def discover( - cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs + cls, *, context: DistributionFinder.Context | None = None, **kwargs ) -> Iterable[Distribution]: """Return an iterable of Distribution objects for all packages. @@ -480,7 +518,7 @@ class Distribution(metaclass=abc.ABCMeta): return filter(None, declared) @property - def metadata(self) -> _meta.PackageMetadata: + def metadata(self) -> _meta.PackageMetadata | None: """Return the parsed metadata for this Distribution. The returned object will have keys that name the various bits of @@ -490,10 +528,8 @@ class Distribution(metaclass=abc.ABCMeta): Custom providers may provide the METADATA file or override this property. """ - # deferred for performance (python/cpython#109829) - from . import _adapters - opt_text = ( + text = ( self.read_text('METADATA') or self.read_text('PKG-INFO') # This last clause is here to support old egg-info files. Its @@ -501,13 +537,20 @@ class Distribution(metaclass=abc.ABCMeta): # (which points to the egg-info file) attribute unchanged. or self.read_text('') ) - text = cast(str, opt_text) + return self._assemble_message(text) + + @staticmethod + @pass_none + def _assemble_message(text: str) -> _meta.PackageMetadata: + # deferred for performance (python/cpython#109829) + from . import _adapters + return _adapters.Message(email.message_from_string(text)) @property def name(self) -> str: """Return the 'Name' metadata for the distribution package.""" - return self.metadata['Name'] + return md_none(self.metadata)['Name'] @property def _normalized_name(self): @@ -517,7 +560,7 @@ class Distribution(metaclass=abc.ABCMeta): @property def version(self) -> str: """Return the 'Version' metadata for the distribution package.""" - return self.metadata['Version'] + return md_none(self.metadata)['Version'] @property def entry_points(self) -> EntryPoints: @@ -530,7 +573,7 @@ class Distribution(metaclass=abc.ABCMeta): return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) @property - def files(self) -> Optional[List[PackagePath]]: + def files(self) -> list[PackagePath] | None: """Files in this distribution. :return: List of PackagePath for this distribution or None @@ -622,7 +665,7 @@ class Distribution(metaclass=abc.ABCMeta): return text and map('"{}"'.format, text.splitlines()) @property - def requires(self) -> Optional[List[str]]: + def requires(self) -> list[str] | None: """Generated requirements specified for this Distribution""" reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() return reqs and list(reqs) @@ -728,7 +771,7 @@ class DistributionFinder(MetaPathFinder): vars(self).update(kwargs) @property - def path(self) -> List[str]: + def path(self) -> list[str]: """ The sequence of directory path that a distribution finder should search. @@ -880,7 +923,7 @@ class Prepared: normalized = None legacy_normalized = None - def __init__(self, name: Optional[str]): + def __init__(self, name: str | None): self.name = name if name is None: return @@ -950,7 +993,7 @@ class PathDistribution(Distribution): """ self._path = path - def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]: + def read_text(self, filename: str | os.PathLike[str]) -> str | None: with suppress( FileNotFoundError, IsADirectoryError, @@ -1067,7 +1110,7 @@ def distributions(**kwargs) -> Iterable[Distribution]: return Distribution.discover(**kwargs) -def metadata(distribution_name: str) -> _meta.PackageMetadata: +def metadata(distribution_name: str) -> _meta.PackageMetadata | None: """Get the metadata for the named package. :param distribution_name: The name of the distribution package to query. @@ -1110,7 +1153,7 @@ def entry_points(**params) -> EntryPoints: return EntryPoints(eps).select(**params) -def files(distribution_name: str) -> Optional[List[PackagePath]]: +def files(distribution_name: str) -> list[PackagePath] | None: """Return a list of files for the named package. :param distribution_name: The name of the distribution package to query. @@ -1119,7 +1162,7 @@ def files(distribution_name: str) -> Optional[List[PackagePath]]: return distribution(distribution_name).files -def requires(distribution_name: str) -> Optional[List[str]]: +def requires(distribution_name: str) -> list[str] | None: """ Return a list of requirements for the named package. @@ -1129,7 +1172,7 @@ def requires(distribution_name: str) -> Optional[List[str]]: return distribution(distribution_name).requires -def packages_distributions() -> Mapping[str, List[str]]: +def packages_distributions() -> Mapping[str, list[str]]: """ Return a mapping of top-level packages to their distributions. @@ -1142,7 +1185,7 @@ def packages_distributions() -> Mapping[str, List[str]]: pkg_to_dist = collections.defaultdict(list) for dist in distributions(): for pkg in _top_level_declared(dist) or _top_level_inferred(dist): - pkg_to_dist[pkg].append(dist.metadata['Name']) + pkg_to_dist[pkg].append(md_none(dist.metadata)['Name']) return dict(pkg_to_dist) @@ -1150,7 +1193,7 @@ def _top_level_declared(dist): return (dist.read_text('top_level.txt') or '').split() -def _topmost(name: PackagePath) -> Optional[str]: +def _topmost(name: PackagePath) -> str | None: """ Return the top-most parent as long as there is a parent. """ diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/_collections.py b/contrib/python/importlib-metadata/py3/importlib_metadata/_collections.py index cf0954e1a30..fc5045d36be 100644 --- a/contrib/python/importlib-metadata/py3/importlib_metadata/_collections.py +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/_collections.py @@ -1,4 +1,5 @@ import collections +import typing # from jaraco.collections 3.3 @@ -24,7 +25,10 @@ class FreezableDefaultDict(collections.defaultdict): self._frozen = lambda key: self.default_factory() -class Pair(collections.namedtuple('Pair', 'name value')): +class Pair(typing.NamedTuple): + name: str + value: str + @classmethod def parse(cls, text): return cls(*map(str.strip, text.split("=", 1))) diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/_meta.py b/contrib/python/importlib-metadata/py3/importlib_metadata/_meta.py index 0942bbd963a..0c20eff3da7 100644 --- a/contrib/python/importlib-metadata/py3/importlib_metadata/_meta.py +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/_meta.py @@ -1,15 +1,11 @@ from __future__ import annotations import os +from collections.abc import Iterator from typing import ( Any, - Dict, - Iterator, - List, - Optional, Protocol, TypeVar, - Union, overload, ) @@ -28,25 +24,25 @@ class PackageMetadata(Protocol): @overload def get( self, name: str, failobj: None = None - ) -> Optional[str]: ... # pragma: no cover + ) -> str | None: ... # pragma: no cover @overload - def get(self, name: str, failobj: _T) -> Union[str, _T]: ... # pragma: no cover + def get(self, name: str, failobj: _T) -> str | _T: ... # pragma: no cover # overload per python/importlib_metadata#435 @overload def get_all( self, name: str, failobj: None = None - ) -> Optional[List[Any]]: ... # pragma: no cover + ) -> list[Any] | None: ... # pragma: no cover @overload - def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]: + def get_all(self, name: str, failobj: _T) -> list[Any] | _T: """ Return all values associated with a possibly multi-valued key. """ @property - def json(self) -> Dict[str, Union[str, List[str]]]: + def json(self) -> dict[str, str | list[str]]: """ A JSON-compatible form of the metadata. """ @@ -58,11 +54,11 @@ class SimplePath(Protocol): """ def joinpath( - self, other: Union[str, os.PathLike[str]] + self, other: str | os.PathLike[str] ) -> SimplePath: ... # pragma: no cover def __truediv__( - self, other: Union[str, os.PathLike[str]] + self, other: str | os.PathLike[str] ) -> SimplePath: ... # pragma: no cover @property diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/_typing.py b/contrib/python/importlib-metadata/py3/importlib_metadata/_typing.py new file mode 100644 index 00000000000..32b1d2b98ac --- /dev/null +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/_typing.py @@ -0,0 +1,15 @@ +import functools +import typing + +from ._meta import PackageMetadata + +md_none = functools.partial(typing.cast, PackageMetadata) +""" +Suppress type errors for optional metadata. + +Although Distribution.metadata can return None when metadata is corrupt +and thus None, allow callers to assume it's not None and crash if +that's the case. + +# python/importlib_metadata#493 +""" diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/compat/py39.py b/contrib/python/importlib-metadata/py3/importlib_metadata/compat/py39.py index 1f15bd97e6a..3eb9c01ecbb 100644 --- a/contrib/python/importlib-metadata/py3/importlib_metadata/compat/py39.py +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/compat/py39.py @@ -2,7 +2,9 @@ Compatibility layer with Python 3.8/3.9 """ -from typing import TYPE_CHECKING, Any, Optional +from __future__ import annotations + +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: # pragma: no cover # Prevent circular imports on runtime. @@ -10,8 +12,10 @@ if TYPE_CHECKING: # pragma: no cover else: Distribution = EntryPoint = Any +from .._typing import md_none + -def normalized_name(dist: Distribution) -> Optional[str]: +def normalized_name(dist: Distribution) -> str | None: """ Honor name normalization for distributions that don't provide ``_normalized_name``. """ @@ -20,7 +24,9 @@ def normalized_name(dist: Distribution) -> Optional[str]: except AttributeError: from .. import Prepared # -> delay to prevent circular imports. - return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name']) + return Prepared.normalize( + getattr(dist, "name", None) or md_none(dist.metadata)['Name'] + ) def ep_matches(ep: EntryPoint, **params) -> bool: diff --git a/contrib/python/importlib-metadata/py3/ya.make b/contrib/python/importlib-metadata/py3/ya.make index bd8f1d06d9a..9544a14830f 100644 --- a/contrib/python/importlib-metadata/py3/ya.make +++ b/contrib/python/importlib-metadata/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(8.6.1) +VERSION(8.7.0) LICENSE(Apache-2.0) @@ -18,6 +18,7 @@ PY_SRCS( importlib_metadata/_itertools.py importlib_metadata/_meta.py importlib_metadata/_text.py + importlib_metadata/_typing.py importlib_metadata/compat/__init__.py importlib_metadata/compat/py311.py importlib_metadata/compat/py39.py |
