Skip to content

Commit

Permalink
Merge branch 'maint/2.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Dec 2, 2020
2 parents 9aee90b + 83c7dd2 commit 155bc48
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 23 deletions.
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
v3.1.1
======

v2.1.1
======

* #261: Restored compatibility for package discovery for
metadata without version in the name and for legacy
eggs.

v3.1.0
======

Expand Down
59 changes: 36 additions & 23 deletions importlib_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,47 +448,29 @@ def zip_children(self):

return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)

def is_egg(self, search):
base = self.base
def search(self, name):
return (
base == search.versionless_egg_name
or base.startswith(search.prefix)
and base.endswith('.egg')
self.joinpath(child)
for child in self.children()
if name.matches(child, self.base)
)

def search(self, name):
for child in self.children():
n_low = child.lower()
if (
n_low in name.exact_matches
or n_low.replace('.', '_').startswith(name.prefix)
and n_low.endswith(name.suffixes)
# legacy case:
or self.is_egg(name)
and n_low == 'egg-info'
):
yield self.joinpath(child)


class Prepared:
"""
A prepared search for metadata on a possibly-named package.
"""

normalized = ''
prefix = ''
normalized = None
suffixes = '.dist-info', '.egg-info'
exact_matches = [''][:0]
versionless_egg_name = ''

def __init__(self, name):
self.name = name
if name is None:
return
self.normalized = self.normalize(name)
self.prefix = self.normalized + '-'
self.exact_matches = [self.normalized + suffix for suffix in self.suffixes]
self.versionless_egg_name = self.normalized + '.egg'

@staticmethod
def normalize(name):
Expand All @@ -497,6 +479,37 @@ def normalize(name):
"""
return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')

@staticmethod
def legacy_normalize(name):
"""
Normalize the package name as found in the convention in
older packaging tools versions and specs.
"""
return name.lower().replace('-', '_')

def matches(self, cand, base):
low = cand.lower()
pre, ext = os.path.splitext(low)
name, sep, rest = pre.partition('-')
return (
low in self.exact_matches
or ext in self.suffixes
and (not self.normalized or name.replace('.', '_') == self.normalized)
# legacy case:
or self.is_egg(base)
and low == 'egg-info'
)

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)
and base.endswith('.egg')
)


@install
class MetadataPathFinder(NullFinder, DistributionFinder):
Expand Down
6 changes: 6 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir):
Version: 1.0.0
""",
},
"pkg.lot.egg-info": {
"METADATA": """
Name: pkg.lot
Version: 1.0.0
""",
},
}

def setUp(self):
Expand Down
13 changes: 13 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ def test_name_normalization(self):
with self.subTest(name):
assert distribution(name).metadata['Name'] == 'pkg.dot'

def test_prefix_not_matched(self):
prefixes = 'p', 'pkg', 'pkg.'
for prefix in prefixes:
with self.subTest(prefix):
with self.assertRaises(PackageNotFoundError):
distribution(prefix)

def test_for_top_level(self):
self.assertEqual(
distribution('egginfo-pkg').read_text('top_level.txt').strip(), 'mod'
Expand Down Expand Up @@ -160,6 +167,12 @@ def test_name_normalization(self):
with self.subTest(name):
assert distribution(name).metadata['Name'] == 'pkg.dot'

def test_name_normalization_versionless_egg_info(self):
names = 'pkg.lot', 'pkg_lot', 'pkg-lot', 'pkg..lot', 'Pkg.Lot'
for name in names:
with self.subTest(name):
assert distribution(name).metadata['Name'] == 'pkg.lot'


class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase):
def test_find_distributions_specified_path(self):
Expand Down

0 comments on commit 155bc48

Please sign in to comment.