https://github.com/python/cpython/commit/cbf9b8cc08364cdcf355fe1c897f698331b49a41
commit: cbf9b8cc08364cdcf355fe1c897f698331b49a41
branch: main
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2026-01-13T08:54:15+02:00
summary:

gh-143658: importlib.metadata: Use `str.translate` to improve performance of 
`importlib.metadata.Prepared.normalized` (#143660)

Co-authored-by: Henry Schreiner <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Bartosz Sławecki <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-01-10-15-40-57.gh-issue-143658.Ox6pE5.rst
M Lib/importlib/metadata/__init__.py
M Lib/test/test_importlib/metadata/test_api.py

diff --git a/Lib/importlib/metadata/__init__.py 
b/Lib/importlib/metadata/__init__.py
index b010bb8525e5cc..9b723b4ec15e12 100644
--- a/Lib/importlib/metadata/__init__.py
+++ b/Lib/importlib/metadata/__init__.py
@@ -890,6 +890,14 @@ def search(self, prepared: Prepared):
         return itertools.chain(infos, eggs)
 
 
+# Translation table for Prepared.normalize: lowercase and
+# replace "-" (hyphen) and "." (dot) with "_" (underscore).
+_normalize_table = str.maketrans(
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ-.",
+    "abcdefghijklmnopqrstuvwxyz__",
+)
+
+
 class Prepared:
     """
     A prepared search query for metadata on a possibly-named package.
@@ -925,7 +933,13 @@ def normalize(name):
         """
         PEP 503 normalization plus dashes as underscores.
         """
-        return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
+        # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503
+        # About 3x faster, safe since packages only support alphanumeric 
characters
+        value = name.translate(_normalize_table)
+        # Condense repeats (faster than regex)
+        while "__" in value:
+            value = value.replace("__", "_")
+        return value
 
     @staticmethod
     def legacy_normalize(name):
diff --git a/Lib/test/test_importlib/metadata/test_api.py 
b/Lib/test/test_importlib/metadata/test_api.py
index 9f6e12c87e859c..3c856a88b77bf6 100644
--- a/Lib/test/test_importlib/metadata/test_api.py
+++ b/Lib/test/test_importlib/metadata/test_api.py
@@ -6,6 +6,7 @@
 from importlib.metadata import (
     Distribution,
     PackageNotFoundError,
+    Prepared,
     distribution,
     entry_points,
     files,
@@ -313,3 +314,36 @@ class InvalidateCache(unittest.TestCase):
     def test_invalidate_cache(self):
         # No externally observable behavior, but ensures test coverage...
         importlib.invalidate_caches()
+
+
+class PreparedTests(unittest.TestCase):
+    def test_normalize(self):
+        tests = [
+            # Simple
+            ("sample", "sample"),
+            # Mixed case
+            ("Sample", "sample"),
+            ("SAMPLE", "sample"),
+            ("SaMpLe", "sample"),
+            # Separator conversions
+            ("sample-pkg", "sample_pkg"),
+            ("sample.pkg", "sample_pkg"),
+            ("sample_pkg", "sample_pkg"),
+            # Multiple separators
+            ("sample---pkg", "sample_pkg"),
+            ("sample___pkg", "sample_pkg"),
+            ("sample...pkg", "sample_pkg"),
+            # Mixed separators
+            ("sample-._pkg", "sample_pkg"),
+            ("sample_.-pkg", "sample_pkg"),
+            # Complex
+            ("Sample__Pkg-name.foo", "sample_pkg_name_foo"),
+            ("Sample__Pkg.name__foo", "sample_pkg_name_foo"),
+            # Uppercase with separators
+            ("SAMPLE-PKG", "sample_pkg"),
+            ("Sample.Pkg", "sample_pkg"),
+            ("SAMPLE_PKG", "sample_pkg"),
+        ]
+        for name, expected in tests:
+            with self.subTest(name=name):
+                self.assertEqual(Prepared.normalize(name), expected)
diff --git 
a/Misc/NEWS.d/next/Library/2026-01-10-15-40-57.gh-issue-143658.Ox6pE5.rst 
b/Misc/NEWS.d/next/Library/2026-01-10-15-40-57.gh-issue-143658.Ox6pE5.rst
new file mode 100644
index 00000000000000..1d22709572641b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-10-15-40-57.gh-issue-143658.Ox6pE5.rst
@@ -0,0 +1,3 @@
+:mod:`importlib.metadata`: Use :meth:`str.translate` to improve performance of
+:meth:`!importlib.metadata.Prepared.normalize`. Patch by Hugo van Kemenade and
+Henry Schreiner.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to