mirror of
https://github.com/python/cpython.git
synced 2025-11-24 20:30:18 +00:00
Revert "gh-132947: Apply changes from importlib_metadata 8.7 (#137885)" (#137924)
Some checks are pending
Tests / Windows MSI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Some checks are pending
Tests / Windows MSI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
This reverts commit 5292fc00f2.
This commit is contained in:
parent
8750e5ecfc
commit
3706ef66ef
12 changed files with 120 additions and 292 deletions
|
|
@ -1,40 +1,33 @@
|
|||
"""
|
||||
APIs exposing metadata from third-party Python packages.
|
||||
|
||||
This codebase is shared between importlib.metadata in the stdlib
|
||||
and importlib_metadata in PyPI. See
|
||||
https://github.com/python/importlib_metadata/wiki/Development-Methodology
|
||||
for more detail.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import abc
|
||||
import collections
|
||||
import sys
|
||||
import json
|
||||
import email
|
||||
import types
|
||||
import inspect
|
||||
import pathlib
|
||||
import zipfile
|
||||
import operator
|
||||
import textwrap
|
||||
import functools
|
||||
import itertools
|
||||
import operator
|
||||
import os
|
||||
import pathlib
|
||||
import posixpath
|
||||
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
|
||||
import collections
|
||||
|
||||
from . import _meta
|
||||
from ._collections import FreezableDefaultDict, Pair
|
||||
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 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
|
||||
|
||||
__all__ = [
|
||||
'Distribution',
|
||||
|
|
@ -60,7 +53,7 @@ class PackageNotFoundError(ModuleNotFoundError):
|
|||
return f"No package metadata was found for {self.name}"
|
||||
|
||||
@property
|
||||
def name(self) -> str: # type: ignore[override] # make readonly
|
||||
def name(self) -> str: # type: ignore[override]
|
||||
(name,) = self.args
|
||||
return name
|
||||
|
||||
|
|
@ -130,12 +123,6 @@ 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.
|
||||
|
||||
|
|
@ -151,30 +138,6 @@ 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(
|
||||
|
|
@ -202,44 +165,38 @@ class EntryPoint:
|
|||
value: str
|
||||
group: str
|
||||
|
||||
dist: Distribution | None = None
|
||||
dist: Optional[Distribution] = 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.
|
||||
"""
|
||||
module = import_module(self.module)
|
||||
attrs = filter(None, (self.attr or '').split('.'))
|
||||
match = cast(Match, self.pattern.match(self.value))
|
||||
module = import_module(match.group('module'))
|
||||
attrs = filter(None, (match.group('attr') or '').split('.'))
|
||||
return functools.reduce(getattr, attrs, module)
|
||||
|
||||
@property
|
||||
def module(self) -> str:
|
||||
return self._match.module
|
||||
match = self.pattern.match(self.value)
|
||||
assert match is not None
|
||||
return match.group('module')
|
||||
|
||||
@property
|
||||
def attr(self) -> str:
|
||||
return self._match.attr
|
||||
match = self.pattern.match(self.value)
|
||||
assert match is not None
|
||||
return match.group('attr')
|
||||
|
||||
@property
|
||||
def extras(self) -> list[str]:
|
||||
return re.findall(r'\w+', self._match.extras or '')
|
||||
|
||||
@functools.cached_property
|
||||
def _match(self) -> _EntryPointMatch:
|
||||
def extras(self) -> List[str]:
|
||||
match = self.pattern.match(self.value)
|
||||
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())
|
||||
assert match is not None
|
||||
return re.findall(r'\w+', match.group('extras') or '')
|
||||
|
||||
def _for(self, dist):
|
||||
vars(self).update(dist=dist)
|
||||
|
|
@ -265,26 +222,9 @@ class EntryPoint:
|
|||
>>> ep.matches(attr='bong')
|
||||
True
|
||||
"""
|
||||
self._disallow_dist(params)
|
||||
attrs = (getattr(self, param) for param in params)
|
||||
return all(map(operator.eq, params.values(), attrs))
|
||||
|
||||
@staticmethod
|
||||
def _disallow_dist(params):
|
||||
"""
|
||||
Querying by dist is not allowed (dist objects are not comparable).
|
||||
>>> EntryPoint(name='fan', value='fav', group='fag').matches(dist='foo')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: "dist" is not suitable for matching...
|
||||
"""
|
||||
if "dist" in params:
|
||||
raise ValueError(
|
||||
'"dist" is not suitable for matching. '
|
||||
"Instead, use Distribution.entry_points.select() on a "
|
||||
"located distribution."
|
||||
)
|
||||
|
||||
def _key(self):
|
||||
return self.name, self.value, self.group
|
||||
|
||||
|
|
@ -314,7 +254,7 @@ class EntryPoints(tuple):
|
|||
|
||||
__slots__ = ()
|
||||
|
||||
def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] # Work with str instead of int
|
||||
def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override]
|
||||
"""
|
||||
Get the EntryPoint in self matching name.
|
||||
"""
|
||||
|
|
@ -338,14 +278,14 @@ class EntryPoints(tuple):
|
|||
return EntryPoints(ep for ep in self if ep.matches(**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.
|
||||
"""
|
||||
|
|
@ -366,11 +306,11 @@ class EntryPoints(tuple):
|
|||
class PackagePath(pathlib.PurePosixPath):
|
||||
"""A reference to a path in a package"""
|
||||
|
||||
hash: FileHash | None
|
||||
hash: Optional[FileHash]
|
||||
size: int
|
||||
dist: Distribution
|
||||
|
||||
def read_text(self, encoding: str = 'utf-8') -> str:
|
||||
def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override]
|
||||
return self.locate().read_text(encoding=encoding)
|
||||
|
||||
def read_binary(self) -> bytes:
|
||||
|
|
@ -401,7 +341,7 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def read_text(self, filename) -> str | None:
|
||||
def read_text(self, filename) -> Optional[str]:
|
||||
"""Attempt to load metadata file given by the name.
|
||||
|
||||
Python distribution metadata is organized by blobs of text
|
||||
|
|
@ -428,17 +368,6 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
"""
|
||||
Given a path to a file in this distribution, return a SimplePath
|
||||
to it.
|
||||
|
||||
This method is used by callers of ``Distribution.files()`` to
|
||||
locate files within the distribution. If it's possible for a
|
||||
Distribution to represent files in the distribution as
|
||||
``SimplePath`` objects, it should implement this method
|
||||
to resolve such objects.
|
||||
|
||||
Some Distribution providers may elect not to resolve SimplePath
|
||||
objects within the distribution by raising a
|
||||
NotImplementedError, but consumers of such a Distribution would
|
||||
be unable to invoke ``Distribution.files()``.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
|
@ -461,7 +390,7 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
|
||||
@classmethod
|
||||
def discover(
|
||||
cls, *, context: DistributionFinder.Context | None = None, **kwargs
|
||||
cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs
|
||||
) -> Iterable[Distribution]:
|
||||
"""Return an iterable of Distribution objects for all packages.
|
||||
|
||||
|
|
@ -507,7 +436,7 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
return filter(None, declared)
|
||||
|
||||
@property
|
||||
def metadata(self) -> _meta.PackageMetadata | None:
|
||||
def metadata(self) -> _meta.PackageMetadata:
|
||||
"""Return the parsed metadata for this Distribution.
|
||||
|
||||
The returned object will have keys that name the various bits of
|
||||
|
|
@ -517,8 +446,10 @@ 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
|
||||
|
||||
text = (
|
||||
opt_text = (
|
||||
self.read_text('METADATA')
|
||||
or self.read_text('PKG-INFO')
|
||||
# This last clause is here to support old egg-info files. Its
|
||||
|
|
@ -526,20 +457,13 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
# (which points to the egg-info file) attribute unchanged.
|
||||
or self.read_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
|
||||
|
||||
text = cast(str, opt_text)
|
||||
return _adapters.Message(email.message_from_string(text))
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the 'Name' metadata for the distribution package."""
|
||||
return md_none(self.metadata)['Name']
|
||||
return self.metadata['Name']
|
||||
|
||||
@property
|
||||
def _normalized_name(self):
|
||||
|
|
@ -549,7 +473,7 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
@property
|
||||
def version(self) -> str:
|
||||
"""Return the 'Version' metadata for the distribution package."""
|
||||
return md_none(self.metadata)['Version']
|
||||
return self.metadata['Version']
|
||||
|
||||
@property
|
||||
def entry_points(self) -> EntryPoints:
|
||||
|
|
@ -562,7 +486,7 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
|
||||
|
||||
@property
|
||||
def files(self) -> list[PackagePath] | None:
|
||||
def files(self) -> Optional[List[PackagePath]]:
|
||||
"""Files in this distribution.
|
||||
|
||||
:return: List of PackagePath for this distribution or None
|
||||
|
|
@ -655,7 +579,7 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
return text and map('"{}"'.format, text.splitlines())
|
||||
|
||||
@property
|
||||
def requires(self) -> list[str] | None:
|
||||
def requires(self) -> Optional[List[str]]:
|
||||
"""Generated requirements specified for this Distribution"""
|
||||
reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
|
||||
return reqs and list(reqs)
|
||||
|
|
@ -711,9 +635,6 @@ class Distribution(metaclass=abc.ABCMeta):
|
|||
return self._load_json('direct_url.json')
|
||||
|
||||
def _load_json(self, filename):
|
||||
# Deferred for performance (python/importlib_metadata#503)
|
||||
import json
|
||||
|
||||
return pass_none(json.loads)(
|
||||
self.read_text(filename),
|
||||
object_hook=lambda data: types.SimpleNamespace(**data),
|
||||
|
|
@ -761,7 +682,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.
|
||||
|
|
@ -798,7 +719,7 @@ class FastPath:
|
|||
True
|
||||
"""
|
||||
|
||||
@functools.lru_cache() # type: ignore[misc]
|
||||
@functools.lru_cache() # type: ignore
|
||||
def __new__(cls, root):
|
||||
return super().__new__(cls)
|
||||
|
||||
|
|
@ -816,9 +737,6 @@ class FastPath:
|
|||
return []
|
||||
|
||||
def zip_children(self):
|
||||
# deferred for performance (python/importlib_metadata#502)
|
||||
import zipfile
|
||||
|
||||
zip_path = zipfile.Path(self.root)
|
||||
names = zip_path.root.namelist()
|
||||
self.joinpath = zip_path.joinpath
|
||||
|
|
@ -913,7 +831,7 @@ class Prepared:
|
|||
normalized = None
|
||||
legacy_normalized = None
|
||||
|
||||
def __init__(self, name: str | None):
|
||||
def __init__(self, name: Optional[str]):
|
||||
self.name = name
|
||||
if name is None:
|
||||
return
|
||||
|
|
@ -976,7 +894,7 @@ class PathDistribution(Distribution):
|
|||
"""
|
||||
self._path = path
|
||||
|
||||
def read_text(self, filename: str | os.PathLike[str]) -> str | None:
|
||||
def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]:
|
||||
with suppress(
|
||||
FileNotFoundError,
|
||||
IsADirectoryError,
|
||||
|
|
@ -1040,7 +958,7 @@ def distributions(**kwargs) -> Iterable[Distribution]:
|
|||
return Distribution.discover(**kwargs)
|
||||
|
||||
|
||||
def metadata(distribution_name: str) -> _meta.PackageMetadata | None:
|
||||
def metadata(distribution_name: str) -> _meta.PackageMetadata:
|
||||
"""Get the metadata for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package to query.
|
||||
|
|
@ -1083,7 +1001,7 @@ def entry_points(**params) -> EntryPoints:
|
|||
return EntryPoints(eps).select(**params)
|
||||
|
||||
|
||||
def files(distribution_name: str) -> list[PackagePath] | None:
|
||||
def files(distribution_name: str) -> Optional[List[PackagePath]]:
|
||||
"""Return a list of files for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package to query.
|
||||
|
|
@ -1092,7 +1010,7 @@ def files(distribution_name: str) -> list[PackagePath] | None:
|
|||
return distribution(distribution_name).files
|
||||
|
||||
|
||||
def requires(distribution_name: str) -> list[str] | None:
|
||||
def requires(distribution_name: str) -> Optional[List[str]]:
|
||||
"""
|
||||
Return a list of requirements for the named package.
|
||||
|
||||
|
|
@ -1102,7 +1020,7 @@ def requires(distribution_name: str) -> list[str] | None:
|
|||
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.
|
||||
|
|
@ -1115,7 +1033,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(md_none(dist.metadata)['Name'])
|
||||
pkg_to_dist[pkg].append(dist.metadata['Name'])
|
||||
return dict(pkg_to_dist)
|
||||
|
||||
|
||||
|
|
@ -1123,7 +1041,7 @@ def _top_level_declared(dist):
|
|||
return (dist.read_text('top_level.txt') or '').split()
|
||||
|
||||
|
||||
def _topmost(name: PackagePath) -> str | None:
|
||||
def _topmost(name: PackagePath) -> Optional[str]:
|
||||
"""
|
||||
Return the top-most parent as long as there is a parent.
|
||||
"""
|
||||
|
|
@ -1149,10 +1067,11 @@ def _get_toplevel_name(name: PackagePath) -> str:
|
|||
>>> _get_toplevel_name(PackagePath('foo.dist-info'))
|
||||
'foo.dist-info'
|
||||
"""
|
||||
# Defer import of inspect for performance (python/cpython#118761)
|
||||
import inspect
|
||||
|
||||
return _topmost(name) or inspect.getmodulename(name) or str(name)
|
||||
return _topmost(name) or (
|
||||
# python/typeshed#10328
|
||||
inspect.getmodulename(name) # type: ignore
|
||||
or str(name)
|
||||
)
|
||||
|
||||
|
||||
def _top_level_inferred(dist):
|
||||
|
|
|
|||
|
|
@ -1,58 +1,11 @@
|
|||
import email.message
|
||||
import email.policy
|
||||
import re
|
||||
import textwrap
|
||||
import email.message
|
||||
|
||||
from ._text import FoldedCase
|
||||
|
||||
|
||||
class RawPolicy(email.policy.EmailPolicy):
|
||||
def fold(self, name, value):
|
||||
folded = self.linesep.join(
|
||||
textwrap.indent(value, prefix=' ' * 8, predicate=lambda line: True)
|
||||
.lstrip()
|
||||
.splitlines()
|
||||
)
|
||||
return f'{name}: {folded}{self.linesep}'
|
||||
|
||||
|
||||
class Message(email.message.Message):
|
||||
r"""
|
||||
Specialized Message subclass to handle metadata naturally.
|
||||
|
||||
Reads values that may have newlines in them and converts the
|
||||
payload to the Description.
|
||||
|
||||
>>> msg_text = textwrap.dedent('''
|
||||
... Name: Foo
|
||||
... Version: 3.0
|
||||
... License: blah
|
||||
... de-blah
|
||||
... <BLANKLINE>
|
||||
... First line of description.
|
||||
... Second line of description.
|
||||
... <BLANKLINE>
|
||||
... Fourth line!
|
||||
... ''').lstrip().replace('<BLANKLINE>', '')
|
||||
>>> msg = Message(email.message_from_string(msg_text))
|
||||
>>> msg['Description']
|
||||
'First line of description.\nSecond line of description.\n\nFourth line!\n'
|
||||
|
||||
Message should render even if values contain newlines.
|
||||
|
||||
>>> print(msg)
|
||||
Name: Foo
|
||||
Version: 3.0
|
||||
License: blah
|
||||
de-blah
|
||||
Description: First line of description.
|
||||
Second line of description.
|
||||
<BLANKLINE>
|
||||
Fourth line!
|
||||
<BLANKLINE>
|
||||
<BLANKLINE>
|
||||
"""
|
||||
|
||||
multiple_use_keys = set(
|
||||
map(
|
||||
FoldedCase,
|
||||
|
|
@ -104,20 +57,15 @@ class Message(email.message.Message):
|
|||
def _repair_headers(self):
|
||||
def redent(value):
|
||||
"Correct for RFC822 indentation"
|
||||
indent = ' ' * 8
|
||||
if not value or '\n' + indent not in value:
|
||||
if not value or '\n' not in value:
|
||||
return value
|
||||
return textwrap.dedent(indent + value)
|
||||
return textwrap.dedent(' ' * 8 + value)
|
||||
|
||||
headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
|
||||
if self._payload:
|
||||
headers.append(('Description', self.get_payload()))
|
||||
self.set_payload('')
|
||||
return headers
|
||||
|
||||
def as_string(self):
|
||||
return super().as_string(policy=RawPolicy())
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import collections
|
||||
import typing
|
||||
|
||||
|
||||
# from jaraco.collections 3.3
|
||||
|
|
@ -25,10 +24,7 @@ class FreezableDefaultDict(collections.defaultdict):
|
|||
self._frozen = lambda key: self.default_factory()
|
||||
|
||||
|
||||
class Pair(typing.NamedTuple):
|
||||
name: str
|
||||
value: str
|
||||
|
||||
class Pair(collections.namedtuple('Pair', 'name value')):
|
||||
@classmethod
|
||||
def parse(cls, text):
|
||||
return cls(*map(str.strip, text.split("=", 1)))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import functools
|
||||
import types
|
||||
import functools
|
||||
|
||||
|
||||
# from jaraco.functools 3.3
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from collections.abc import Iterator
|
||||
from typing import (
|
||||
Any,
|
||||
Protocol,
|
||||
TypeVar,
|
||||
overload,
|
||||
)
|
||||
from typing import Protocol
|
||||
from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
|
@ -24,25 +20,25 @@ class PackageMetadata(Protocol):
|
|||
@overload
|
||||
def get(
|
||||
self, name: str, failobj: None = None
|
||||
) -> str | None: ... # pragma: no cover
|
||||
) -> Optional[str]: ... # pragma: no cover
|
||||
|
||||
@overload
|
||||
def get(self, name: str, failobj: _T) -> str | _T: ... # pragma: no cover
|
||||
def get(self, name: str, failobj: _T) -> Union[str, _T]: ... # pragma: no cover
|
||||
|
||||
# overload per python/importlib_metadata#435
|
||||
@overload
|
||||
def get_all(
|
||||
self, name: str, failobj: None = None
|
||||
) -> list[Any] | None: ... # pragma: no cover
|
||||
) -> Optional[List[Any]]: ... # pragma: no cover
|
||||
|
||||
@overload
|
||||
def get_all(self, name: str, failobj: _T) -> list[Any] | _T:
|
||||
def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
|
||||
"""
|
||||
Return all values associated with a possibly multi-valued key.
|
||||
"""
|
||||
|
||||
@property
|
||||
def json(self) -> dict[str, str | list[str]]:
|
||||
def json(self) -> Dict[str, Union[str, List[str]]]:
|
||||
"""
|
||||
A JSON-compatible form of the metadata.
|
||||
"""
|
||||
|
|
@ -54,11 +50,11 @@ class SimplePath(Protocol):
|
|||
"""
|
||||
|
||||
def joinpath(
|
||||
self, other: str | os.PathLike[str]
|
||||
self, other: Union[str, os.PathLike[str]]
|
||||
) -> SimplePath: ... # pragma: no cover
|
||||
|
||||
def __truediv__(
|
||||
self, other: str | os.PathLike[str]
|
||||
self, other: Union[str, os.PathLike[str]]
|
||||
) -> SimplePath: ... # pragma: no cover
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
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
|
||||
"""
|
||||
|
|
@ -1,14 +1,9 @@
|
|||
# from jaraco.path 3.7.2
|
||||
|
||||
from __future__ import annotations
|
||||
# from jaraco.path 3.7
|
||||
|
||||
import functools
|
||||
import pathlib
|
||||
from collections.abc import Mapping
|
||||
from typing import TYPE_CHECKING, Protocol, Union, runtime_checkable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Self
|
||||
from typing import Dict, Protocol, Union
|
||||
from typing import runtime_checkable
|
||||
|
||||
|
||||
class Symlink(str):
|
||||
|
|
@ -17,25 +12,29 @@ class Symlink(str):
|
|||
"""
|
||||
|
||||
|
||||
FilesSpec = Mapping[str, Union[str, bytes, Symlink, 'FilesSpec']]
|
||||
FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']] # type: ignore
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class TreeMaker(Protocol):
|
||||
def __truediv__(self, other, /) -> Self: ...
|
||||
def mkdir(self, *, exist_ok) -> object: ...
|
||||
def write_text(self, content, /, *, encoding) -> object: ...
|
||||
def write_bytes(self, content, /) -> object: ...
|
||||
def symlink_to(self, target, /) -> object: ...
|
||||
def __truediv__(self, *args, **kwargs): ... # pragma: no cover
|
||||
|
||||
def mkdir(self, **kwargs): ... # pragma: no cover
|
||||
|
||||
def write_text(self, content, **kwargs): ... # pragma: no cover
|
||||
|
||||
def write_bytes(self, content): ... # pragma: no cover
|
||||
|
||||
def symlink_to(self, target): ... # pragma: no cover
|
||||
|
||||
|
||||
def _ensure_tree_maker(obj: str | TreeMaker) -> TreeMaker:
|
||||
return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj)
|
||||
def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker:
|
||||
return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore
|
||||
|
||||
|
||||
def build(
|
||||
spec: FilesSpec,
|
||||
prefix: str | TreeMaker = pathlib.Path(),
|
||||
prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore
|
||||
):
|
||||
"""
|
||||
Build a set of files/directories, as described by the spec.
|
||||
|
|
@ -67,24 +66,23 @@ def build(
|
|||
|
||||
|
||||
@functools.singledispatch
|
||||
def create(content: str | bytes | FilesSpec, path: TreeMaker) -> None:
|
||||
def create(content: Union[str, bytes, FilesSpec], path):
|
||||
path.mkdir(exist_ok=True)
|
||||
# Mypy only looks at the signature of the main singledispatch method. So it must contain the complete Union
|
||||
build(content, prefix=path) # type: ignore[arg-type] # python/mypy#11727
|
||||
build(content, prefix=path) # type: ignore
|
||||
|
||||
|
||||
@create.register
|
||||
def _(content: bytes, path: TreeMaker) -> None:
|
||||
def _(content: bytes, path):
|
||||
path.write_bytes(content)
|
||||
|
||||
|
||||
@create.register
|
||||
def _(content: str, path: TreeMaker) -> None:
|
||||
def _(content: str, path):
|
||||
path.write_text(content, encoding='utf-8')
|
||||
|
||||
|
||||
@create.register
|
||||
def _(content: Symlink, path: TreeMaker) -> None:
|
||||
def _(content: Symlink, path):
|
||||
path.symlink_to(content)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import contextlib
|
||||
import copy
|
||||
import functools
|
||||
import json
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
import copy
|
||||
import json
|
||||
import shutil
|
||||
import pathlib
|
||||
import textwrap
|
||||
import functools
|
||||
import contextlib
|
||||
|
||||
from test.support import import_helper
|
||||
from test.support import os_helper
|
||||
|
|
@ -14,10 +14,14 @@ from test.support import requires_zlib
|
|||
from . import _path
|
||||
from ._path import FilesSpec
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from importlib import resources
|
||||
else:
|
||||
import importlib_resources as resources
|
||||
|
||||
try:
|
||||
from importlib import resources # type: ignore
|
||||
|
||||
getattr(resources, 'files')
|
||||
getattr(resources, 'as_file')
|
||||
except (ImportError, AttributeError):
|
||||
import importlib_resources as resources # type: ignore
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import importlib
|
||||
import re
|
||||
import textwrap
|
||||
import unittest
|
||||
import importlib
|
||||
|
||||
from . import fixtures
|
||||
from importlib.metadata import (
|
||||
Distribution,
|
||||
PackageNotFoundError,
|
||||
|
|
@ -14,8 +15,6 @@ from importlib.metadata import (
|
|||
version,
|
||||
)
|
||||
|
||||
from . import fixtures
|
||||
|
||||
|
||||
class APITests(
|
||||
fixtures.EggInfoPkg,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import importlib
|
||||
import pickle
|
||||
import re
|
||||
import pickle
|
||||
import unittest
|
||||
import importlib
|
||||
import importlib.metadata
|
||||
from test.support import os_helper
|
||||
|
||||
try:
|
||||
|
|
@ -9,6 +10,8 @@ try:
|
|||
except ImportError:
|
||||
from .stubs import fake_filesystem_unittest as ffs
|
||||
|
||||
from . import fixtures
|
||||
from ._path import Symlink
|
||||
from importlib.metadata import (
|
||||
Distribution,
|
||||
EntryPoint,
|
||||
|
|
@ -21,9 +24,6 @@ from importlib.metadata import (
|
|||
version,
|
||||
)
|
||||
|
||||
from . import fixtures
|
||||
from ._path import Symlink
|
||||
|
||||
|
||||
class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
|
||||
version_pattern = r'\d+\.\d+(\.\d)?'
|
||||
|
|
@ -157,16 +157,6 @@ class InvalidMetadataTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCa
|
|||
dist = Distribution.from_name('foo')
|
||||
assert dist.version == "1.0"
|
||||
|
||||
def test_missing_metadata(self):
|
||||
"""
|
||||
Dists with a missing metadata file should return None.
|
||||
|
||||
Ref python/importlib_metadata#493.
|
||||
"""
|
||||
fixtures.build_files(self.make_pkg('foo-4.3', files={}), self.site_dir)
|
||||
assert Distribution.from_name('foo').metadata is None
|
||||
assert metadata('foo') is None
|
||||
|
||||
|
||||
class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import sys
|
||||
import unittest
|
||||
|
||||
from . import fixtures
|
||||
from importlib.metadata import (
|
||||
PackageNotFoundError,
|
||||
distribution,
|
||||
|
|
@ -10,8 +11,6 @@ from importlib.metadata import (
|
|||
version,
|
||||
)
|
||||
|
||||
from . import fixtures
|
||||
|
||||
|
||||
class TestZip(fixtures.ZipFixtures, unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
Applied changes to ``importlib.metadata`` from `importlib_metadata 8.7
|
||||
<https://importlib-metadata.readthedocs.io/en/latest/history.html#v8-7-0>`_,
|
||||
including ``dist`` now disallowed for ``EntryPoints.select``; deferred
|
||||
imports for faster import times; added support for metadata with newlines
|
||||
(python/cpython#119650); and ``metadata()`` function now returns ``None``
|
||||
when a metadata directory is present but no metadata is present.
|
||||
Loading…
Add table
Add a link
Reference in a new issue