[Python-checkins] bpo-39297: Update for importlib_metadata 1.4. (GH-17947)

Jason R. Coombs webhook-mailer at python.org
Sat Jan 11 10:37:33 EST 2020


https://github.com/python/cpython/commit/136735c1a2efb320e4cbb64b40f1191228745b39
commit: 136735c1a2efb320e4cbb64b40f1191228745b39
branch: master
author: Jason R. Coombs <jaraco at jaraco.com>
committer: GitHub <noreply at github.com>
date: 2020-01-11T10:37:28-05:00
summary:

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>

files:
A Misc/NEWS.d/next/Library/2020-01-11-01-15-37.bpo-39297.y98Z6Q.rst
M Lib/importlib/metadata.py

diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py
index 53f9fb5934668..ae8ecf9b8500c 100644
--- a/Lib/importlib/metadata.py
+++ b/Lib/importlib/metadata.py
@@ -10,6 +10,7 @@
 import operator
 import functools
 import itertools
+import posixpath
 import collections
 
 from configparser import ConfigParser
@@ -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()):
         """
@@ -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()):
@@ -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):
diff --git a/Misc/NEWS.d/next/Library/2020-01-11-01-15-37.bpo-39297.y98Z6Q.rst b/Misc/NEWS.d/next/Library/2020-01-11-01-15-37.bpo-39297.y98Z6Q.rst
new file mode 100644
index 0000000000000..618f6f9f2b7ff
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-01-11-01-15-37.bpo-39297.y98Z6Q.rst
@@ -0,0 +1 @@
+Improved performance of importlib.metadata distribution discovery and resilients to inaccessible sys.path entries (importlib_metadata v1.4.0).



More information about the Python-checkins mailing list