Skip to content

Commit bd6367c

Browse files
committed
Merge branch 'feature/refactor-fewer-finders' into 'master'
A simpler implementation with fewer finders Closes #60 and #59 See merge request python-devs/importlib_metadata!60
2 parents 6074bfa + 497afeb commit bd6367c

File tree

5 files changed

+26
-87
lines changed

5 files changed

+26
-87
lines changed

importlib_metadata/_hooks.py

Lines changed: 18 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,21 @@
1111

1212
if sys.version_info >= (3,): # pragma: nocover
1313
from contextlib import suppress
14-
from pathlib import Path
1514
else: # pragma: nocover
1615
from contextlib2 import suppress # noqa
1716
from itertools import imap as map # type: ignore
18-
from pathlib2 import Path
1917

2018
FileNotFoundError = IOError, OSError
2119
NotADirectoryError = IOError, OSError
2220
__metaclass__ = type
2321

2422

23+
if sys.version_info > (3, 5): # pragma: nocover
24+
from pathlib import Path
25+
else: # pragma: nocover
26+
from pathlib2 import Path
27+
28+
2529
def install(cls):
2630
"""Class decorator for installation on sys.meta_path."""
2731
sys.meta_path.append(cls())
@@ -46,12 +50,14 @@ def find_spec(*args, **kwargs):
4650
find_module = find_spec
4751

4852

49-
class MetadataPathBaseFinder(NullFinder):
53+
@install
54+
class MetadataPathFinder(NullFinder):
5055
"""A degenerate finder for distribution packages on the file system.
5156
5257
This finder supplies only a find_distributions() method for versions
5358
of Python that do not have a PathFinder find_distributions().
5459
"""
60+
search_template = r'{pattern}(-.*)?\.(dist|egg)-info'
5561

5662
def find_distributions(self, name=None, path=None):
5763
"""Return an iterable of all Distribution instances capable of
@@ -72,9 +78,15 @@ def _search_paths(cls, pattern, paths):
7278
"""
7379
return itertools.chain.from_iterable(
7480
cls._search_path(path, pattern)
75-
for path in map(Path, paths)
81+
for path in map(cls._switch_path, paths)
7682
)
7783

84+
@staticmethod
85+
def _switch_path(path):
86+
with suppress(Exception):
87+
return zipp.Path(path)
88+
return Path(path)
89+
7890
@classmethod
7991
def _predicate(cls, pattern, root, item):
8092
return re.match(pattern, str(item.name), flags=re.IGNORECASE)
@@ -89,82 +101,15 @@ def _search_path(cls, root, pattern):
89101
if cls._predicate(matcher, root, item))
90102

91103

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):
103-
return (
104-
(root / item).is_file() and
105-
re.match(pattern, str(item.name), flags=re.IGNORECASE))
106-
107-
108104
class PathDistribution(Distribution):
109105
def __init__(self, path):
110106
"""Construct a distribution from a path to the metadata directory."""
111107
self._path = path
112108

113109
def read_text(self, filename):
114-
with suppress(FileNotFoundError, NotADirectoryError):
115-
with self._path.joinpath(filename).open(encoding='utf-8') as fp:
116-
return fp.read()
117-
return None
110+
with suppress(FileNotFoundError, NotADirectoryError, KeyError):
111+
return self._path.joinpath(filename).read_text(encoding='utf-8')
118112
read_text.__doc__ = Distribution.read_text.__doc__
119113

120114
def locate_file(self, path):
121115
return self._path.parent / path
122-
123-
124-
@install
125-
class WheelMetadataFinder(NullFinder):
126-
"""A degenerate finder for distribution packages in wheels.
127-
128-
This finder supplies only a find_distributions() method for versions
129-
of Python that do not have a PathFinder find_distributions().
130-
"""
131-
search_template = r'{pattern}(-.*)?\.whl'
132-
133-
def find_distributions(self, name=None, path=None):
134-
"""Return an iterable of all Distribution instances capable of
135-
loading the metadata for packages matching the name
136-
(or all names if not supplied) along the paths in the list
137-
of directories ``path`` (defaults to sys.path).
138-
"""
139-
if path is None:
140-
path = sys.path
141-
pattern = '.*' if name is None else re.escape(name)
142-
found = self._search_paths(pattern, path)
143-
return map(WheelDistribution, found)
144-
145-
@classmethod
146-
def _search_paths(cls, pattern, paths):
147-
return (
148-
path
149-
for path in map(Path, paths)
150-
if re.match(
151-
cls.search_template.format(pattern=pattern),
152-
str(path.name),
153-
flags=re.IGNORECASE,
154-
)
155-
)
156-
157-
158-
class WheelDistribution(Distribution):
159-
def __init__(self, archive):
160-
self._archive = zipp.Path(archive)
161-
name, version = archive.name.split('-')[0:2]
162-
self._dist_info = '{}-{}.dist-info'.format(name, version)
163-
164-
def read_text(self, filename):
165-
target = self._archive / self._dist_info / filename
166-
return target.read_text() if target.exists() else None
167-
read_text.__doc__ = Distribution.read_text.__doc__
168-
169-
def locate_file(self, path):
170-
return self._archive / path

importlib_metadata/api.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
from itertools import starmap
1616

1717
if sys.version_info > (3,): # pragma: nocover
18-
import pathlib
1918
from configparser import ConfigParser
2019
else: # pragma: nocover
21-
import pathlib2 as pathlib
2220
from backports.configparser import ConfigParser
2321
from itertools import imap as map # type: ignore
2422

23+
if sys.version_info > (3, 5): # pragma: nocover
24+
import pathlib
25+
else: # pragma: nocover
26+
import pathlib2 as pathlib
27+
2528
try:
2629
BaseClass = ModuleNotFoundError
2730
except NameError: # pragma: nocover

importlib_metadata/docs/using.rst

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,10 @@ Python's ``site-packages`` directory via tools such as `pip
1818
<https://pypi.org/project/pip/>`_. Specifically,
1919
it means a package with either a discoverable ``dist-info`` or ``egg-info``
2020
directory, and metadata defined by `PEP 566`_ or its older specifications.
21-
By default, package metadata can live on the file system or in wheels on
21+
By default, package metadata can live on the file system or in zip archives on
2222
``sys.path``. Through an extension mechanism, the metadata can live almost
2323
anywhere.
2424

25-
.. note:: Although this package supports loading metadata from wheels
26-
on ``sys.path``, that support is provisional and does not serve to
27-
contravene the `PEP 427 directive
28-
<https://www.python.org/dev/peps/pep-0427/#is-it-possible-to-import-python-code-directly-from-a-wheel-file>`_,
29-
which states that relying on this format is discouraged, and use is
30-
at your own risk.
31-
3225

3326
Overview
3427
========

importlib_metadata/tests/test_main.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ def test_for_name_does_not_exist(self):
3232
def test_new_style_classes(self):
3333
self.assertIsInstance(Distribution, type)
3434
self.assertIsInstance(_hooks.MetadataPathFinder, type)
35-
self.assertIsInstance(_hooks.WheelMetadataFinder, type)
36-
self.assertIsInstance(_hooks.WheelDistribution, type)
3735

3836

3937
class ImportTests(fixtures.DistInfoPkg, unittest.TestCase):

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ classifiers =
1717
[options]
1818
python_requires = >=2.7,!=3.0,!=3.1,!=3.2,!=3.3
1919
install_requires =
20-
zipp>=0.3.2
21-
pathlib2; python_version < '3'
20+
zipp>=0.5
21+
pathlib2; python_version=='3.4.*' or python_version < '3'
2222
contextlib2; python_version < '3'
2323
configparser; python_version < '3'
2424
packages = find:

0 commit comments

Comments
 (0)