Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 667294d

Browse files
authored
gh-117089: Apply changes from importlib_metadata 7.1.0 (#117094)
* Apply changes from importlib_metadata 7.1.0 * Include the data sources in the makefile (even though they're not needed)
1 parent f4cc77d commit 667294d

File tree

10 files changed

+212
-95
lines changed

10 files changed

+212
-95
lines changed

Lib/importlib/metadata/__init__.py

Lines changed: 123 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import os
24
import re
35
import abc
@@ -26,7 +28,7 @@
2628
from importlib import import_module
2729
from importlib.abc import MetaPathFinder
2830
from itertools import starmap
29-
from typing import Iterable, List, Mapping, Optional, Set, Union, cast
31+
from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast
3032

3133
__all__ = [
3234
'Distribution',
@@ -163,17 +165,17 @@ class EntryPoint:
163165
value: str
164166
group: str
165167

166-
dist: Optional['Distribution'] = None
168+
dist: Optional[Distribution] = None
167169

168170
def __init__(self, name: str, value: str, group: str) -> None:
169171
vars(self).update(name=name, value=value, group=group)
170172

171-
def load(self):
173+
def load(self) -> Any:
172174
"""Load the entry point from its definition. If only a module
173175
is indicated by the value, return that module. Otherwise,
174176
return the named object.
175177
"""
176-
match = self.pattern.match(self.value)
178+
match = cast(Match, self.pattern.match(self.value))
177179
module = import_module(match.group('module'))
178180
attrs = filter(None, (match.group('attr') or '').split('.'))
179181
return functools.reduce(getattr, attrs, module)
@@ -268,7 +270,7 @@ def __repr__(self):
268270
"""
269271
return '%s(%r)' % (self.__class__.__name__, tuple(self))
270272

271-
def select(self, **params):
273+
def select(self, **params) -> EntryPoints:
272274
"""
273275
Select entry points from self that match the
274276
given parameters (typically group and/or name).
@@ -304,19 +306,17 @@ def _from_text(text):
304306
class PackagePath(pathlib.PurePosixPath):
305307
"""A reference to a path in a package"""
306308

307-
hash: Optional["FileHash"]
309+
hash: Optional[FileHash]
308310
size: int
309-
dist: "Distribution"
311+
dist: Distribution
310312

311313
def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override]
312-
with self.locate().open(encoding=encoding) as stream:
313-
return stream.read()
314+
return self.locate().read_text(encoding=encoding)
314315

315316
def read_binary(self) -> bytes:
316-
with self.locate().open('rb') as stream:
317-
return stream.read()
317+
return self.locate().read_bytes()
318318

319-
def locate(self) -> pathlib.Path:
319+
def locate(self) -> SimplePath:
320320
"""Return a path-like object for this path"""
321321
return self.dist.locate_file(self)
322322

@@ -330,6 +330,7 @@ def __repr__(self) -> str:
330330

331331

332332
class DeprecatedNonAbstract:
333+
# Required until Python 3.14
333334
def __new__(cls, *args, **kwargs):
334335
all_names = {
335336
name for subclass in inspect.getmro(cls) for name in vars(subclass)
@@ -349,25 +350,48 @@ def __new__(cls, *args, **kwargs):
349350

350351

351352
class Distribution(DeprecatedNonAbstract):
352-
"""A Python distribution package."""
353+
"""
354+
An abstract Python distribution package.
355+
356+
Custom providers may derive from this class and define
357+
the abstract methods to provide a concrete implementation
358+
for their environment. Some providers may opt to override
359+
the default implementation of some properties to bypass
360+
the file-reading mechanism.
361+
"""
353362

354363
@abc.abstractmethod
355364
def read_text(self, filename) -> Optional[str]:
356365
"""Attempt to load metadata file given by the name.
357366
367+
Python distribution metadata is organized by blobs of text
368+
typically represented as "files" in the metadata directory
369+
(e.g. package-1.0.dist-info). These files include things
370+
like:
371+
372+
- METADATA: The distribution metadata including fields
373+
like Name and Version and Description.
374+
- entry_points.txt: A series of entry points as defined in
375+
`the entry points spec <https://packaging.python.org/en/latest/specifications/entry-points/#file-format>`_.
376+
- RECORD: A record of files according to
377+
`this recording spec <https://packaging.python.org/en/latest/specifications/recording-installed-packages/#the-record-file>`_.
378+
379+
A package may provide any set of files, including those
380+
not listed here or none at all.
381+
358382
:param filename: The name of the file in the distribution info.
359383
:return: The text if found, otherwise None.
360384
"""
361385

362386
@abc.abstractmethod
363-
def locate_file(self, path: Union[str, os.PathLike[str]]) -> pathlib.Path:
387+
def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
364388
"""
365-
Given a path to a file in this distribution, return a path
389+
Given a path to a file in this distribution, return a SimplePath
366390
to it.
367391
"""
368392

369393
@classmethod
370-
def from_name(cls, name: str) -> "Distribution":
394+
def from_name(cls, name: str) -> Distribution:
371395
"""Return the Distribution for the given package name.
372396
373397
:param name: The name of the distribution package to search for.
@@ -385,16 +409,18 @@ def from_name(cls, name: str) -> "Distribution":
385409
raise PackageNotFoundError(name)
386410

387411
@classmethod
388-
def discover(cls, **kwargs) -> Iterable["Distribution"]:
412+
def discover(
413+
cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs
414+
) -> Iterable[Distribution]:
389415
"""Return an iterable of Distribution objects for all packages.
390416
391417
Pass a ``context`` or pass keyword arguments for constructing
392418
a context.
393419
394420
:context: A ``DistributionFinder.Context`` object.
395-
:return: Iterable of Distribution objects for all packages.
421+
:return: Iterable of Distribution objects for packages matching
422+
the context.
396423
"""
397-
context = kwargs.pop('context', None)
398424
if context and kwargs:
399425
raise ValueError("cannot accept context and kwargs")
400426
context = context or DistributionFinder.Context(**kwargs)
@@ -403,8 +429,8 @@ def discover(cls, **kwargs) -> Iterable["Distribution"]:
403429
)
404430

405431
@staticmethod
406-
def at(path: Union[str, os.PathLike[str]]) -> "Distribution":
407-
"""Return a Distribution for the indicated metadata path
432+
def at(path: str | os.PathLike[str]) -> Distribution:
433+
"""Return a Distribution for the indicated metadata path.
408434
409435
:param path: a string or path-like object
410436
:return: a concrete Distribution instance for the path
@@ -413,7 +439,7 @@ def at(path: Union[str, os.PathLike[str]]) -> "Distribution":
413439

414440
@staticmethod
415441
def _discover_resolvers():
416-
"""Search the meta_path for resolvers."""
442+
"""Search the meta_path for resolvers (MetadataPathFinders)."""
417443
declared = (
418444
getattr(finder, 'find_distributions', None) for finder in sys.meta_path
419445
)
@@ -424,7 +450,11 @@ def metadata(self) -> _meta.PackageMetadata:
424450
"""Return the parsed metadata for this Distribution.
425451
426452
The returned object will have keys that name the various bits of
427-
metadata. See PEP 566 for details.
453+
metadata per the
454+
`Core metadata specifications <https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata>`_.
455+
456+
Custom providers may provide the METADATA file or override this
457+
property.
428458
"""
429459
opt_text = (
430460
self.read_text('METADATA')
@@ -454,6 +484,12 @@ def version(self) -> str:
454484

455485
@property
456486
def entry_points(self) -> EntryPoints:
487+
"""
488+
Return EntryPoints for this distribution.
489+
490+
Custom providers may provide the ``entry_points.txt`` file
491+
or override this property.
492+
"""
457493
return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
458494

459495
@property
@@ -466,6 +502,10 @@ def files(self) -> Optional[List[PackagePath]]:
466502
(i.e. RECORD for dist-info, or installed-files.txt or
467503
SOURCES.txt for egg-info) is missing.
468504
Result may be empty if the metadata exists but is empty.
505+
506+
Custom providers are recommended to provide a "RECORD" file (in
507+
``read_text``) or override this property to allow for callers to be
508+
able to resolve filenames provided by the package.
469509
"""
470510

471511
def make_file(name, hash=None, size_str=None):
@@ -497,7 +537,7 @@ def skip_missing_files(package_paths):
497537

498538
def _read_files_distinfo(self):
499539
"""
500-
Read the lines of RECORD
540+
Read the lines of RECORD.
501541
"""
502542
text = self.read_text('RECORD')
503543
return text and text.splitlines()
@@ -611,6 +651,9 @@ def _load_json(self, filename):
611651
class DistributionFinder(MetaPathFinder):
612652
"""
613653
A MetaPathFinder capable of discovering installed distributions.
654+
655+
Custom providers should implement this interface in order to
656+
supply metadata.
614657
"""
615658

616659
class Context:
@@ -623,6 +666,17 @@ class Context:
623666
Each DistributionFinder may expect any parameters
624667
and should attempt to honor the canonical
625668
parameters defined below when appropriate.
669+
670+
This mechanism gives a custom provider a means to
671+
solicit additional details from the caller beyond
672+
"name" and "path" when searching distributions.
673+
For example, imagine a provider that exposes suites
674+
of packages in either a "public" or "private" ``realm``.
675+
A caller may wish to query only for distributions in
676+
a particular realm and could call
677+
``distributions(realm="private")`` to signal to the
678+
custom provider to only include distributions from that
679+
realm.
626680
"""
627681

628682
name = None
@@ -658,11 +712,18 @@ def find_distributions(self, context=Context()) -> Iterable[Distribution]:
658712

659713
class FastPath:
660714
"""
661-
Micro-optimized class for searching a path for
662-
children.
715+
Micro-optimized class for searching a root for children.
716+
717+
Root is a path on the file system that may contain metadata
718+
directories either as natural directories or within a zip file.
663719
664720
>>> FastPath('').children()
665721
['...']
722+
723+
FastPath objects are cached and recycled for any given root.
724+
725+
>>> FastPath('foobar') is FastPath('foobar')
726+
True
666727
"""
667728

668729
@functools.lru_cache() # type: ignore
@@ -704,7 +765,19 @@ def lookup(self, mtime):
704765

705766

706767
class Lookup:
768+
"""
769+
A micro-optimized class for searching a (fast) path for metadata.
770+
"""
771+
707772
def __init__(self, path: FastPath):
773+
"""
774+
Calculate all of the children representing metadata.
775+
776+
From the children in the path, calculate early all of the
777+
children that appear to represent metadata (infos) or legacy
778+
metadata (eggs).
779+
"""
780+
708781
base = os.path.basename(path.root).lower()
709782
base_is_egg = base.endswith(".egg")
710783
self.infos = FreezableDefaultDict(list)
@@ -725,7 +798,10 @@ def __init__(self, path: FastPath):
725798
self.infos.freeze()
726799
self.eggs.freeze()
727800

728-
def search(self, prepared):
801+
def search(self, prepared: Prepared):
802+
"""
803+
Yield all infos and eggs matching the Prepared query.
804+
"""
729805
infos = (
730806
self.infos[prepared.normalized]
731807
if prepared
@@ -741,13 +817,28 @@ def search(self, prepared):
741817

742818
class Prepared:
743819
"""
744-
A prepared search for metadata on a possibly-named package.
820+
A prepared search query for metadata on a possibly-named package.
821+
822+
Pre-calculates the normalization to prevent repeated operations.
823+
824+
>>> none = Prepared(None)
825+
>>> none.normalized
826+
>>> none.legacy_normalized
827+
>>> bool(none)
828+
False
829+
>>> sample = Prepared('Sample__Pkg-name.foo')
830+
>>> sample.normalized
831+
'sample_pkg_name_foo'
832+
>>> sample.legacy_normalized
833+
'sample__pkg_name.foo'
834+
>>> bool(sample)
835+
True
745836
"""
746837

747838
normalized = None
748839
legacy_normalized = None
749840

750-
def __init__(self, name):
841+
def __init__(self, name: Optional[str]):
751842
self.name = name
752843
if name is None:
753844
return
@@ -777,7 +868,7 @@ class MetadataPathFinder(DistributionFinder):
777868
@classmethod
778869
def find_distributions(
779870
cls, context=DistributionFinder.Context()
780-
) -> Iterable["PathDistribution"]:
871+
) -> Iterable[PathDistribution]:
781872
"""
782873
Find distributions.
783874
@@ -810,7 +901,7 @@ def __init__(self, path: SimplePath) -> None:
810901
"""
811902
self._path = path
812903

813-
def read_text(self, filename: Union[str, os.PathLike[str]]) -> Optional[str]:
904+
def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]:
814905
with suppress(
815906
FileNotFoundError,
816907
IsADirectoryError,
@@ -824,7 +915,7 @@ def read_text(self, filename: Union[str, os.PathLike[str]]) -> Optional[str]:
824915

825916
read_text.__doc__ = Distribution.read_text.__doc__
826917

827-
def locate_file(self, path: Union[str, os.PathLike[str]]) -> pathlib.Path:
918+
def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
828919
return self._path.parent / path
829920

830921
@property

0 commit comments

Comments
 (0)