Skip to content

Commit 2ca4591

Browse files
committed
Merge branch 'egginfo-files' into 'master'
Support egg-info files Closes #43 See merge request python-devs/importlib_metadata!57
2 parents 28b9914 + dd4a955 commit 2ca4591

File tree

5 files changed

+78
-25
lines changed

5 files changed

+78
-25
lines changed

importlib_metadata/_hooks.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pathlib2 import Path
1919

2020
FileNotFoundError = IOError, OSError
21+
NotADirectoryError = IOError, OSError
2122
__metaclass__ = type
2223

2324

@@ -45,14 +46,12 @@ def find_spec(*args, **kwargs):
4546
find_module = find_spec
4647

4748

48-
@install
49-
class MetadataPathFinder(NullFinder):
49+
class MetadataPathBaseFinder(NullFinder):
5050
"""A degenerate finder for distribution packages on the file system.
5151
5252
This finder supplies only a find_distributions() method for versions
5353
of Python that do not have a PathFinder find_distributions().
5454
"""
55-
search_template = r'{pattern}(-.*)?\.(dist|egg)-info'
5655

5756
def find_distributions(self, name=None, path=None):
5857
"""Return an iterable of all Distribution instances capable of
@@ -76,21 +75,34 @@ def _search_paths(cls, pattern, paths):
7675
for path in map(Path, paths)
7776
)
7877

78+
@classmethod
79+
def _predicate(cls, pattern, root, item):
80+
return re.match(pattern, str(item.name), flags=re.IGNORECASE)
81+
7982
@classmethod
8083
def _search_path(cls, root, pattern):
8184
if not root.is_dir():
8285
return ()
8386
normalized = pattern.replace('-', '_')
87+
matcher = cls.search_template.format(pattern=normalized)
88+
return (item for item in root.iterdir()
89+
if cls._predicate(matcher, root, item))
90+
91+
92+
@install
93+
class MetadataPathFinder(MetadataPathBaseFinder):
94+
search_template = r'{pattern}(-.*)?\.(dist|egg)-info'
95+
96+
97+
@install
98+
class MetadataPathEggInfoFileFinder(MetadataPathBaseFinder):
99+
search_template = r'{pattern}(-.*)?\.egg-info'
100+
101+
@classmethod
102+
def _predicate(cls, pattern, root, item):
84103
return (
85-
item
86-
for item in root.iterdir()
87-
if item.is_dir()
88-
and re.match(
89-
cls.search_template.format(pattern=normalized),
90-
str(item.name),
91-
flags=re.IGNORECASE,
92-
)
93-
)
104+
(root / item).is_file() and
105+
re.match(pattern, str(item.name), flags=re.IGNORECASE))
94106

95107

96108
class PathDistribution(Distribution):
@@ -99,7 +111,7 @@ def __init__(self, path):
99111
self._path = path
100112

101113
def read_text(self, filename):
102-
with suppress(FileNotFoundError):
114+
with suppress(FileNotFoundError, NotADirectoryError):
103115
with self._path.joinpath(filename).open(encoding='utf-8') as fp:
104116
return fp.read()
105117
return None

importlib_metadata/api.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,14 @@ def metadata(self):
195195
The returned object will have keys that name the various bits of
196196
metadata. See PEP 566 for details.
197197
"""
198-
text = self.read_text('METADATA') or self.read_text('PKG-INFO')
198+
text = (
199+
self.read_text('METADATA')
200+
or self.read_text('PKG-INFO')
201+
# This last clause is here to support old egg-info files. Its
202+
# effect is to just end up using the PathDistribution's self._path
203+
# (which points to the egg-info file) attribute unchanged.
204+
or self.read_text('')
205+
)
199206
return _email_message_from_string(text)
200207

201208
@property
@@ -246,7 +253,7 @@ def _read_dist_info_reqs(self):
246253

247254
def _read_egg_info_reqs(self):
248255
source = self.read_text('requires.txt')
249-
return self._deps_from_requires_text(source)
256+
return source and self._deps_from_requires_text(source)
250257

251258
@classmethod
252259
def _deps_from_requires_text(cls, source):

importlib_metadata/tests/fixtures.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,27 @@ def setUp(self):
127127
build_files(EggInfoPkg.files, prefix=self.site_dir)
128128

129129

130+
class EggInfoFile(SiteDir):
131+
files = {
132+
"egginfo_file.egg-info": """
133+
Metadata-Version: 1.0
134+
Name: egginfo_file
135+
Version: 0.1
136+
Summary: An example package
137+
Home-page: www.example.com
138+
Author: Eric Haffa-Vee
139+
Author-email: [email protected]
140+
License: UNKNOWN
141+
Description: UNKNOWN
142+
Platform: UNKNOWN
143+
""",
144+
}
145+
146+
def setUp(self):
147+
super(EggInfoFile, self).setUp()
148+
build_files(EggInfoFile.files, prefix=self.site_dir)
149+
150+
130151
class LocalPackage:
131152
def setUp(self):
132153
self.fixtures = ExitStack()
@@ -136,15 +157,15 @@ def setUp(self):
136157

137158

138159
def build_files(file_defs, prefix=pathlib.Path()):
139-
"""
140-
Build a set of files/directories, as described by the
141-
file_defs dictionary.
142-
Each key/value pair in the dictionary is interpreted as
143-
a filename/contents
144-
pair. If the contents value is a dictionary, a directory
145-
is created, and the
146-
dictionary interpreted as the files within it, recursively.
160+
"""Build a set of files/directories, as described by the
161+
162+
file_defs dictionary. Each key/value pair in the dictionary is
163+
interpreted as a filename/contents pair. If the contents value is a
164+
dictionary, a directory is created, and the dictionary interpreted
165+
as the files within it, recursively.
166+
147167
For example:
168+
148169
{"README.txt": "A README file",
149170
"foo": {
150171
"__init__.py": "",

importlib_metadata/tests/test_api.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
from __builtin__ import unicode as text
1717

1818

19-
class APITests(fixtures.EggInfoPkg, fixtures.DistInfoPkg, unittest.TestCase):
19+
class APITests(
20+
fixtures.EggInfoPkg,
21+
fixtures.DistInfoPkg,
22+
fixtures.EggInfoFile,
23+
unittest.TestCase):
24+
2025
version_pattern = r'\d+\.\d+(\.\d)?'
2126

2227
def test_retrieves_version_of_self(self):
@@ -100,6 +105,14 @@ def test_files_dist_info(self):
100105
def test_files_egg_info(self):
101106
self._test_files(importlib_metadata.files('egginfo-pkg'))
102107

108+
def test_version_egg_info_file(self):
109+
version = importlib_metadata.version('egginfo-file')
110+
self.assertEqual(version, '0.1')
111+
112+
def test_requires_egg_info_file(self):
113+
requirements = importlib_metadata.requires('egginfo-file')
114+
self.assertIsNone(requirements)
115+
103116
def test_requires(self):
104117
deps = importlib_metadata.requires('egginfo-pkg')
105118
assert any(

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ skip_missing_interpreters = True
1010
install_command =
1111
python -c 'import subprocess, sys; pip_inst_cmd = sys.executable, "-m", "pip", "install"; subprocess.check_call(pip_inst_cmd + ("pip<19.1", )); subprocess.check_call(pip_inst_cmd + tuple(sys.argv[1:]))' {opts} {packages}
1212
commands =
13-
nocov: python -m unittest discover
13+
nocov: python -m unittest discover {posargs}
1414
cov,diffcov: python -m coverage run {[coverage]rc} -m unittest discover {posargs}
1515
cov,diffcov: python -m coverage combine {[coverage]rc}
1616
cov: python -m coverage html {[coverage]rc}

0 commit comments

Comments
 (0)