GeezerVison is showing github.com — gh-143658: importlib.metadata: Use `str.translate` to improve perform… · python/importlib_metadata@3e7f3ac
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboard +3Lines changed: 3 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +:mod:`importlib.metadata`: Use :meth:`str.translate` to improve performance of | | 2 | +:meth:`!importlib.metadata.Prepared.normalize`. Patch by Hugo van Kemenade and | | 3 | +Henry Schreiner. |
Collapse file Copy file name to clipboardExpand all lines: importlib_metadata/__init__.py +15-1Lines changed: 15 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -894,6 +894,14 @@ def search(self, prepared: Prepared): | 894 | 894 | return itertools.chain(infos, eggs) | 895 | 895 |
| 896 | 896 |
| | 897 | +# Translation table for Prepared.normalize: lowercase and | | 898 | +# replace "-" (hyphen) and "." (dot) with "_" (underscore). | | 899 | +_normalize_table = str.maketrans( | | 900 | + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-.", | | 901 | + "abcdefghijklmnopqrstuvwxyz__", | | 902 | +) | | 903 | + | | 904 | + | 897 | 905 | class Prepared: | 898 | 906 | """ | 899 | 907 | A prepared search query for metadata on a possibly-named package. | @@ -929,7 +937,13 @@ def normalize(name): | 929 | 937 | """ | 930 | 938 | PEP 503 normalization plus dashes as underscores. | 931 | 939 | """ | 932 | | - return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') | | 940 | + # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503 | | 941 | + # About 3x faster, safe since packages only support alphanumeric characters | | 942 | + value = name.translate(_normalize_table) | | 943 | + # Condense repeats (faster than regex) | | 944 | + while "__" in value: | | 945 | + value = value.replace("__", "_") | | 946 | + return value | 933 | 947 |
| 934 | 948 | @staticmethod | 935 | 949 | def legacy_normalize(name): | |
Collapse file Copy file name to clipboardExpand all lines: tests/test_api.py +34Lines changed: 34 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 6 | 6 | from importlib_metadata import ( | 7 | 7 | Distribution, | 8 | 8 | PackageNotFoundError, | | 9 | + Prepared, | 9 | 10 | distribution, | 10 | 11 | entry_points, | 11 | 12 | files, | @@ -317,3 +318,36 @@ class InvalidateCache(unittest.TestCase): | 317 | 318 | def test_invalidate_cache(self): | 318 | 319 | # No externally observable behavior, but ensures test coverage... | 319 | 320 | importlib.invalidate_caches() | | 321 | + | | 322 | + | | 323 | +class PreparedTests(unittest.TestCase): | | 324 | + def test_normalize(self): | | 325 | + tests = [ | | 326 | + # Simple | | 327 | + ("sample", "sample"), | | 328 | + # Mixed case | | 329 | + ("Sample", "sample"), | | 330 | + ("SAMPLE", "sample"), | | 331 | + ("SaMpLe", "sample"), | | 332 | + # Separator conversions | | 333 | + ("sample-pkg", "sample_pkg"), | | 334 | + ("sample.pkg", "sample_pkg"), | | 335 | + ("sample_pkg", "sample_pkg"), | | 336 | + # Multiple separators | | 337 | + ("sample---pkg", "sample_pkg"), | | 338 | + ("sample___pkg", "sample_pkg"), | | 339 | + ("sample...pkg", "sample_pkg"), | | 340 | + # Mixed separators | | 341 | + ("sample-._pkg", "sample_pkg"), | | 342 | + ("sample_.-pkg", "sample_pkg"), | | 343 | + # Complex | | 344 | + ("Sample__Pkg-name.foo", "sample_pkg_name_foo"), | | 345 | + ("Sample__Pkg.name__foo", "sample_pkg_name_foo"), | | 346 | + # Uppercase with separators | | 347 | + ("SAMPLE-PKG", "sample_pkg"), | | 348 | + ("Sample.Pkg", "sample_pkg"), | | 349 | + ("SAMPLE_PKG", "sample_pkg"), | | 350 | + ] | | 351 | + for name, expected in tests: | | 352 | + with self.subTest(name=name): | | 353 | + self.assertEqual(Prepared.normalize(name), expected) |
|