From c1af1d6c23938f78cf209062cf92dee5866d7b09 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 10 Jan 2021 19:27:06 +0100 Subject: [PATCH 1/3] Avoid needlessly reinstantiating Prepareds. --- importlib_metadata/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index cb20875b..10e57c96 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -565,8 +565,9 @@ def find_distributions(self, context=DistributionFinder.Context()): @classmethod def _search_paths(cls, name, paths): """Find metadata directories in paths heuristically.""" + prepared = Prepared(name) return itertools.chain.from_iterable( - path.search(Prepared(name)) for path in map(FastPath, paths) + path.search(prepared) for path in map(FastPath, paths) ) From c9c0909656d84f689b7826dc19d5e50e6bdf7e1d Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 10 Jan 2021 19:28:03 +0100 Subject: [PATCH 2/3] Switch from the very slow os.path.splitext to str.rpartition. As a consequence, `ext` doesn't include the leading dot anymore, so change `suffixes` accordingly too. --- importlib_metadata/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 10e57c96..c39596a2 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -493,7 +493,7 @@ class Prepared: """ normalized = None - suffixes = '.dist-info', '.egg-info' + suffixes = 'dist-info', 'egg-info' exact_matches = [''][:0] def __init__(self, name): @@ -501,7 +501,9 @@ def __init__(self, name): if name is None: return self.normalized = self.normalize(name) - self.exact_matches = [self.normalized + suffix for suffix in self.suffixes] + self.exact_matches = [ + self.normalized + '.' + suffix for suffix in self.suffixes + ] @staticmethod def normalize(name): @@ -520,8 +522,10 @@ def legacy_normalize(name): def matches(self, cand, base): low = cand.lower() - pre, ext = os.path.splitext(low) - name, sep, rest = pre.partition('-') + # rpartition is like os.path.splitext, but much faster. They'd only + # differ if pre is empty, but in that case we don't have a match anyways. + pre, _, ext = low.rpartition('.') + name, _, rest = pre.partition('-') return ( low in self.exact_matches or ext in self.suffixes From 7fc4f119985990190554992532286455bd70590b Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 10 Jan 2021 19:31:07 +0100 Subject: [PATCH 3/3] Precompute egg_prefix and versionless_egg_name. --- importlib_metadata/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index c39596a2..1a1c951b 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -495,6 +495,8 @@ class Prepared: normalized = None suffixes = 'dist-info', 'egg-info' exact_matches = [''][:0] + egg_prefix = '' + versionless_egg_name = '' def __init__(self, name): self.name = name @@ -504,6 +506,9 @@ def __init__(self, name): self.exact_matches = [ self.normalized + '.' + suffix for suffix in self.suffixes ] + legacy_normalized = self.legacy_normalize(self.name) + self.egg_prefix = legacy_normalized + '-' + self.versionless_egg_name = legacy_normalized + '.egg' @staticmethod def normalize(name): @@ -536,12 +541,9 @@ def matches(self, cand, base): ) def is_egg(self, base): - normalized = self.legacy_normalize(self.name or '') - prefix = normalized + '-' if normalized else '' - versionless_egg_name = normalized + '.egg' if self.name else '' return ( - base == versionless_egg_name - or base.startswith(prefix) + base == self.versionless_egg_name + or base.startswith(self.egg_prefix) and base.endswith('.egg') )