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.
"""
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):
from importlib.metadata import (
Distribution,
PackageNotFoundError,
+ Prepared,
distribution,
entry_points,
files,
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)