Skip to content

Commit

Permalink
bpo-39297: Update for importlib_metadata 1.4. (pythonGH-17947)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
jaraco and blurb-it[bot] authored Jan 11, 2020
1 parent 5d978a2 commit 136735c
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 36 deletions.
108 changes: 72 additions & 36 deletions Lib/importlib/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import operator
import functools
import itertools
import posixpath
import collections

from configparser import ConfigParser
Expand Down Expand Up @@ -371,10 +372,6 @@ def path(self):
"""
return vars(self).get('path', sys.path)

@property
def pattern(self):
return '.*' if self.name is None else re.escape(self.name)

@abc.abstractmethod
def find_distributions(self, context=Context()):
"""
Expand All @@ -386,6 +383,73 @@ def find_distributions(self, context=Context()):
"""


class FastPath:
"""
Micro-optimized class for searching a path for
children.
"""

def __init__(self, root):
self.root = root

def joinpath(self, child):
return pathlib.Path(self.root, child)

def children(self):
with suppress(Exception):
return os.listdir(self.root or '')
with suppress(Exception):
return self.zip_children()
return []

def zip_children(self):
zip_path = zipfile.Path(self.root)
names = zip_path.root.namelist()
self.joinpath = zip_path.joinpath

return (
posixpath.split(child)[0]
for child in names
)

def is_egg(self, search):
root_n_low = os.path.split(self.root)[1].lower()

return (
root_n_low == search.normalized + '.egg'
or root_n_low.startswith(search.prefix)
and root_n_low.endswith('.egg'))

def search(self, name):
for child in self.children():
n_low = child.lower()
if (n_low in name.exact_matches
or n_low.startswith(name.prefix)
and n_low.endswith(name.suffixes)
# legacy case:
or self.is_egg(name) and n_low == 'egg-info'):
yield self.joinpath(child)


class Prepared:
"""
A prepared search for metadata on a possibly-named package.
"""
normalized = ''
prefix = ''
suffixes = '.dist-info', '.egg-info'
exact_matches = [''][:0]

def __init__(self, name):
self.name = name
if name is None:
return
self.normalized = name.lower().replace('-', '_')
self.prefix = self.normalized + '-'
self.exact_matches = [
self.normalized + suffix for suffix in self.suffixes]


class MetadataPathFinder(DistributionFinder):
@classmethod
def find_distributions(cls, context=DistributionFinder.Context()):
Expand All @@ -397,45 +461,17 @@ def find_distributions(cls, context=DistributionFinder.Context()):
(or all names if ``None`` indicated) along the paths in the list
of directories ``context.path``.
"""
found = cls._search_paths(context.pattern, context.path)
found = cls._search_paths(context.name, context.path)
return map(PathDistribution, found)

@classmethod
def _search_paths(cls, pattern, paths):
def _search_paths(cls, name, paths):
"""Find metadata directories in paths heuristically."""
return itertools.chain.from_iterable(
cls._search_path(path, pattern)
for path in map(cls._switch_path, paths)
path.search(Prepared(name))
for path in map(FastPath, paths)
)

@staticmethod
def _switch_path(path):
PYPY_OPEN_BUG = False
if not PYPY_OPEN_BUG or os.path.isfile(path): # pragma: no branch
with suppress(Exception):
return zipfile.Path(path)
return pathlib.Path(path)

@classmethod
def _matches_info(cls, normalized, item):
template = r'{pattern}(-.*)?\.(dist|egg)-info'
manifest = template.format(pattern=normalized)
return re.match(manifest, item.name, flags=re.IGNORECASE)

@classmethod
def _matches_legacy(cls, normalized, item):
template = r'{pattern}-.*\.egg[\\/]EGG-INFO'
manifest = template.format(pattern=normalized)
return re.search(manifest, str(item), flags=re.IGNORECASE)

@classmethod
def _search_path(cls, root, pattern):
if not root.is_dir():
return ()
normalized = pattern.replace('-', '_')
return (item for item in root.iterdir()
if cls._matches_info(normalized, item)
or cls._matches_legacy(normalized, item))


class PathDistribution(Distribution):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved performance of importlib.metadata distribution discovery and resilients to inaccessible sys.path entries (importlib_metadata v1.4.0).

0 comments on commit 136735c

Please sign in to comment.