Skip to content

Commit 83c7dd2

Browse files
authored
Merge pull request #264 from python/bugfix/261-versionless-egg-info-pre-normalized-fallback
Versionless egg info pre normalized fallback
2 parents dd25a38 + 4cb3bd0 commit 83c7dd2

File tree

4 files changed

+63
-20
lines changed

4 files changed

+63
-20
lines changed

docs/changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
importlib_metadata NEWS
33
=========================
44

5+
v2.1.1
6+
======
7+
8+
* #261: Restored compatibility for package discovery for
9+
metadata without version in the name and for legacy
10+
eggs.
11+
512
v2.1.0
613
======
714

importlib_metadata/__init__.py

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -462,43 +462,29 @@ def zip_children(self):
462462
for child in names
463463
)
464464

465-
def is_egg(self, search):
466-
base = self.base
467-
return (
468-
base == search.versionless_egg_name
469-
or base.startswith(search.prefix)
470-
and base.endswith('.egg'))
471-
472465
def search(self, name):
473-
for child in self.children():
474-
n_low = child.lower()
475-
if (n_low in name.exact_matches
476-
or n_low.replace('.', '_').startswith(name.prefix)
477-
and n_low.endswith(name.suffixes)
478-
# legacy case:
479-
or self.is_egg(name) and n_low == 'egg-info'):
480-
yield self.joinpath(child)
466+
return (
467+
self.joinpath(child)
468+
for child in self.children()
469+
if name.matches(child, self.base)
470+
)
481471

482472

483473
class Prepared:
484474
"""
485475
A prepared search for metadata on a possibly-named package.
486476
"""
487-
normalized = ''
488-
prefix = ''
477+
normalized = None
489478
suffixes = '.dist-info', '.egg-info'
490479
exact_matches = [''][:0]
491-
versionless_egg_name = ''
492480

493481
def __init__(self, name):
494482
self.name = name
495483
if name is None:
496484
return
497485
self.normalized = self.normalize(name)
498-
self.prefix = self.normalized + '-'
499486
self.exact_matches = [
500487
self.normalized + suffix for suffix in self.suffixes]
501-
self.versionless_egg_name = self.normalized + '.egg'
502488

503489
@staticmethod
504490
def normalize(name):
@@ -507,6 +493,37 @@ def normalize(name):
507493
"""
508494
return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
509495

496+
@staticmethod
497+
def legacy_normalize(name):
498+
"""
499+
Normalize the package name as found in the convention in
500+
older packaging tools versions and specs.
501+
"""
502+
return name.lower().replace('-', '_')
503+
504+
def matches(self, cand, base):
505+
low = cand.lower()
506+
pre, ext = os.path.splitext(low)
507+
name, sep, rest = pre.partition('-')
508+
return (
509+
low in self.exact_matches
510+
or ext in self.suffixes and (
511+
not self.normalized or
512+
name.replace('.', '_') == self.normalized
513+
)
514+
# legacy case:
515+
or self.is_egg(base) and low == 'egg-info'
516+
)
517+
518+
def is_egg(self, base):
519+
normalized = self.legacy_normalize(self.name or '')
520+
prefix = normalized + '-' if normalized else ''
521+
versionless_egg_name = normalized + '.egg' if self.name else ''
522+
return (
523+
base == versionless_egg_name
524+
or base.startswith(prefix)
525+
and base.endswith('.egg'))
526+
510527

511528
@install
512529
class MetadataPathFinder(NullFinder, DistributionFinder):

tests/fixtures.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir):
127127
Version: 1.0.0
128128
""",
129129
},
130+
"pkg.lot.egg-info": {
131+
"METADATA": """
132+
Name: pkg.lot
133+
Version: 1.0.0
134+
""",
135+
},
130136
}
131137

132138
def setUp(self):

tests/test_api.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ def test_name_normalization(self):
5252
with self.subTest(name):
5353
assert distribution(name).metadata['Name'] == 'pkg.dot'
5454

55+
def test_prefix_not_matched(self):
56+
prefixes = 'p', 'pkg', 'pkg.'
57+
for prefix in prefixes:
58+
with self.subTest(prefix):
59+
with self.assertRaises(PackageNotFoundError):
60+
distribution(prefix)
61+
5562
def test_for_top_level(self):
5663
self.assertEqual(
5764
distribution('egginfo-pkg').read_text('top_level.txt').strip(),
@@ -174,6 +181,12 @@ def test_name_normalization(self):
174181
with self.subTest(name):
175182
assert distribution(name).metadata['Name'] == 'pkg.dot'
176183

184+
def test_name_normalization_versionless_egg_info(self):
185+
names = 'pkg.lot', 'pkg_lot', 'pkg-lot', 'pkg..lot', 'Pkg.Lot'
186+
for name in names:
187+
with self.subTest(name):
188+
assert distribution(name).metadata['Name'] == 'pkg.lot'
189+
177190

178191
class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase):
179192
def test_find_distributions_specified_path(self):

0 commit comments

Comments
 (0)