Skip to content

Commit 136735c

Browse files
bpo-39297: Update for importlib_metadata 1.4. (GH-17947)
* bpo-39297: Update for importlib_metadata 1.4. Includes performance updates. * πŸ“œπŸ€– Added by blurb_it. * Update blurb Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
1 parent 5d978a2 commit 136735c

File tree

2 files changed

+73
-36
lines changed

2 files changed

+73
-36
lines changed

β€ŽLib/importlib/metadata.py

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import operator
1111
import functools
1212
import itertools
13+
import posixpath
1314
import collections
1415

1516
from configparser import ConfigParser
@@ -371,10 +372,6 @@ def path(self):
371372
"""
372373
return vars(self).get('path', sys.path)
373374

374-
@property
375-
def pattern(self):
376-
return '.*' if self.name is None else re.escape(self.name)
377-
378375
@abc.abstractmethod
379376
def find_distributions(self, context=Context()):
380377
"""
@@ -386,6 +383,73 @@ def find_distributions(self, context=Context()):
386383
"""
387384

388385

386+
class FastPath:
387+
"""
388+
Micro-optimized class for searching a path for
389+
children.
390+
"""
391+
392+
def __init__(self, root):
393+
self.root = root
394+
395+
def joinpath(self, child):
396+
return pathlib.Path(self.root, child)
397+
398+
def children(self):
399+
with suppress(Exception):
400+
return os.listdir(self.root or '')
401+
with suppress(Exception):
402+
return self.zip_children()
403+
return []
404+
405+
def zip_children(self):
406+
zip_path = zipfile.Path(self.root)
407+
names = zip_path.root.namelist()
408+
self.joinpath = zip_path.joinpath
409+
410+
return (
411+
posixpath.split(child)[0]
412+
for child in names
413+
)
414+
415+
def is_egg(self, search):
416+
root_n_low = os.path.split(self.root)[1].lower()
417+
418+
return (
419+
root_n_low == search.normalized + '.egg'
420+
or root_n_low.startswith(search.prefix)
421+
and root_n_low.endswith('.egg'))
422+
423+
def search(self, name):
424+
for child in self.children():
425+
n_low = child.lower()
426+
if (n_low in name.exact_matches
427+
or n_low.startswith(name.prefix)
428+
and n_low.endswith(name.suffixes)
429+
# legacy case:
430+
or self.is_egg(name) and n_low == 'egg-info'):
431+
yield self.joinpath(child)
432+
433+
434+
class Prepared:
435+
"""
436+
A prepared search for metadata on a possibly-named package.
437+
"""
438+
normalized = ''
439+
prefix = ''
440+
suffixes = '.dist-info', '.egg-info'
441+
exact_matches = [''][:0]
442+
443+
def __init__(self, name):
444+
self.name = name
445+
if name is None:
446+
return
447+
self.normalized = name.lower().replace('-', '_')
448+
self.prefix = self.normalized + '-'
449+
self.exact_matches = [
450+
self.normalized + suffix for suffix in self.suffixes]
451+
452+
389453
class MetadataPathFinder(DistributionFinder):
390454
@classmethod
391455
def find_distributions(cls, context=DistributionFinder.Context()):
@@ -397,45 +461,17 @@ def find_distributions(cls, context=DistributionFinder.Context()):
397461
(or all names if ``None`` indicated) along the paths in the list
398462
of directories ``context.path``.
399463
"""
400-
found = cls._search_paths(context.pattern, context.path)
464+
found = cls._search_paths(context.name, context.path)
401465
return map(PathDistribution, found)
402466

403467
@classmethod
404-
def _search_paths(cls, pattern, paths):
468+
def _search_paths(cls, name, paths):
405469
"""Find metadata directories in paths heuristically."""
406470
return itertools.chain.from_iterable(
407-
cls._search_path(path, pattern)
408-
for path in map(cls._switch_path, paths)
471+
path.search(Prepared(name))
472+
for path in map(FastPath, paths)
409473
)
410474

411-
@staticmethod
412-
def _switch_path(path):
413-
PYPY_OPEN_BUG = False
414-
if not PYPY_OPEN_BUG or os.path.isfile(path): # pragma: no branch
415-
with suppress(Exception):
416-
return zipfile.Path(path)
417-
return pathlib.Path(path)
418-
419-
@classmethod
420-
def _matches_info(cls, normalized, item):
421-
template = r'{pattern}(-.*)?\.(dist|egg)-info'
422-
manifest = template.format(pattern=normalized)
423-
return re.match(manifest, item.name, flags=re.IGNORECASE)
424-
425-
@classmethod
426-
def _matches_legacy(cls, normalized, item):
427-
template = r'{pattern}-.*\.egg[\\/]EGG-INFO'
428-
manifest = template.format(pattern=normalized)
429-
return re.search(manifest, str(item), flags=re.IGNORECASE)
430-
431-
@classmethod
432-
def _search_path(cls, root, pattern):
433-
if not root.is_dir():
434-
return ()
435-
normalized = pattern.replace('-', '_')
436-
return (item for item in root.iterdir()
437-
if cls._matches_info(normalized, item)
438-
or cls._matches_legacy(normalized, item))
439475

440476

441477
class PathDistribution(Distribution):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved performance of importlib.metadata distribution discovery and resilients to inaccessible sys.path entries (importlib_metadata v1.4.0).

0 commit comments

Comments
Β (0)