Revert "gh-132947: Apply changes from importlib_metadata 8.7 (#137885)" (#137924)
This reverts commit 5292fc00f29df60af82b55fca6c8b360e3667f96.
diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py
index 1e2cea4..b59587e 100644
--- a/Lib/importlib/metadata/__init__.py
+++ b/Lib/importlib/metadata/__init__.py
@@ -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 @@ def __str__(self) -> str:
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 @@ def valid(line: str):
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 @@ def matches(self, **params):
>>> 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 @@ def select(self, **params) -> EntryPoints:
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 @@ def _from_text(text):
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 @@ def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
"""
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 @@ def from_name(cls, name: str) -> Distribution:
@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 @@ def _discover_resolvers():
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 @@ def metadata(self) -> _meta.PackageMetadata | None:
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 @@ def metadata(self) -> _meta.PackageMetadata | None:
# (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 @@ def _normalized_name(self):
@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 @@ def entry_points(self) -> EntryPoints:
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 @@ def _read_files_egginfo_sources(self):
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 @@ def origin(self):
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 @@ def __init__(self, **kwargs):
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 @@ def children(self):
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 @@ def __init__(self, path: SimplePath) -> None:
"""
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):
diff --git a/Lib/importlib/metadata/_adapters.py b/Lib/importlib/metadata/_adapters.py
index f5b30dd..6223263 100644
--- a/Lib/importlib/metadata/_adapters.py
+++ b/Lib/importlib/metadata/_adapters.py
@@ -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 @@ def __getitem__(self, item):
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):
"""
diff --git a/Lib/importlib/metadata/_collections.py b/Lib/importlib/metadata/_collections.py
index fc5045d..cf0954e 100644
--- a/Lib/importlib/metadata/_collections.py
+++ b/Lib/importlib/metadata/_collections.py
@@ -1,5 +1,4 @@
import collections
-import typing
# from jaraco.collections 3.3
@@ -25,10 +24,7 @@ def freeze(self):
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)))
diff --git a/Lib/importlib/metadata/_functools.py b/Lib/importlib/metadata/_functools.py
index 5dda6a2..71f66bd 100644
--- a/Lib/importlib/metadata/_functools.py
+++ b/Lib/importlib/metadata/_functools.py
@@ -1,5 +1,5 @@
-import functools
import types
+import functools
# from jaraco.functools 3.3
diff --git a/Lib/importlib/metadata/_meta.py b/Lib/importlib/metadata/_meta.py
index 0c20eff..1927d0f 100644
--- a/Lib/importlib/metadata/_meta.py
+++ b/Lib/importlib/metadata/_meta.py
@@ -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 @@ def __iter__(self) -> Iterator[str]: ... # pragma: no cover
@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
diff --git a/Lib/importlib/metadata/_typing.py b/Lib/importlib/metadata/_typing.py
deleted file mode 100644
index 32b1d2b..0000000
--- a/Lib/importlib/metadata/_typing.py
+++ /dev/null
@@ -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
-"""
diff --git a/Lib/test/test_importlib/metadata/_path.py b/Lib/test/test_importlib/metadata/_path.py
index e63d889..b3cfb9c 100644
--- a/Lib/test/test_importlib/metadata/_path.py
+++ b/Lib/test/test_importlib/metadata/_path.py
@@ -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)
diff --git a/Lib/test/test_importlib/metadata/fixtures.py b/Lib/test/test_importlib/metadata/fixtures.py
index 494047d..826b1b3 100644
--- a/Lib/test/test_importlib/metadata/fixtures.py
+++ b/Lib/test/test_importlib/metadata/fixtures.py
@@ -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 . 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
diff --git a/Lib/test/test_importlib/metadata/test_api.py b/Lib/test/test_importlib/metadata/test_api.py
index 9f6e12c..813febf 100644
--- a/Lib/test/test_importlib/metadata/test_api.py
+++ b/Lib/test/test_importlib/metadata/test_api.py
@@ -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 @@
version,
)
-from . import fixtures
-
class APITests(
fixtures.EggInfoPkg,
diff --git a/Lib/test/test_importlib/metadata/test_main.py b/Lib/test/test_importlib/metadata/test_main.py
index 83b686b..a0bc822 100644
--- a/Lib/test/test_importlib/metadata/test_main.py
+++ b/Lib/test/test_importlib/metadata/test_main.py
@@ -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 @@
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 @@
version,
)
-from . import fixtures
-from ._path import Symlink
-
class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
version_pattern = r'\d+\.\d+(\.\d)?'
@@ -157,16 +157,6 @@ def test_valid_dists_preferred(self):
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
diff --git a/Lib/test/test_importlib/metadata/test_zip.py b/Lib/test/test_importlib/metadata/test_zip.py
index fcb649f..276f628 100644
--- a/Lib/test/test_importlib/metadata/test_zip.py
+++ b/Lib/test/test_importlib/metadata/test_zip.py
@@ -1,6 +1,7 @@
import sys
import unittest
+from . import fixtures
from importlib.metadata import (
PackageNotFoundError,
distribution,
@@ -10,8 +11,6 @@
version,
)
-from . import fixtures
-
class TestZip(fixtures.ZipFixtures, unittest.TestCase):
def setUp(self):
diff --git a/Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst b/Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst
deleted file mode 100644
index 8a2b0a4..0000000
--- a/Misc/NEWS.d/next/Library/2025-08-17-10-22-31.gh-issue-132947.XR4MJ8.rst
+++ /dev/null
@@ -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.