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

This reverts commit 5292fc00f2.
This commit is contained in:
Jason R. Coombs 2025-08-18 13:57:36 -07:00 committed by GitHub
parent 8750e5ecfc
commit 3706ef66ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 120 additions and 292 deletions

View file

@ -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):

View file

@ -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):
"""

View file

@ -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)))

View file

@ -1,5 +1,5 @@
import functools
import types
import functools
# from jaraco.functools 3.3

View file

@ -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

View file

@ -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
"""

View file

@ -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)

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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):

View file

@ -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.