Skip to content

Commit a4fa9a9

Browse files
bpo-39791: Refresh importlib.metadata from importlib_metadata 1.6.1. (GH-20659) (GH-20661)
* Refresh importlib.metadata from importlib_metadata 1.6.1. * 📜🤖 Added by blurb_it. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> (cherry picked from commit 161541a) Co-authored-by: Jason R. Coombs <[email protected]> Co-authored-by: Jason R. Coombs <[email protected]>
1 parent cdc3d9c commit a4fa9a9

File tree

6 files changed

+84
-18
lines changed

6 files changed

+84
-18
lines changed

‎Doc/library/importlib.metadata.rst‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ Entry points
7777
The ``entry_points()`` function returns a dictionary of all entry points,
7878
keyed by group. Entry points are represented by ``EntryPoint`` instances;
7979
each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
80-
a ``.load()`` method to resolve the value.
80+
a ``.load()`` method to resolve the value. There are also ``.module``,
81+
``.attr``, and ``.extras`` attributes for getting the components of the
82+
``.value`` attribute::
8183

8284
>>> eps = entry_points() # doctest: +SKIP
8385
>>> list(eps) # doctest: +SKIP
@@ -86,6 +88,12 @@ a ``.load()`` method to resolve the value.
8688
>>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] # doctest: +SKIP
8789
>>> wheel # doctest: +SKIP
8890
EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
91+
>>> wheel.module # doctest: +SKIP
92+
'wheel.cli'
93+
>>> wheel.attr # doctest: +SKIP
94+
'main'
95+
>>> wheel.extras # doctest: +SKIP
96+
[]
8997
>>> main = wheel.load() # doctest: +SKIP
9098
>>> main # doctest: +SKIP
9199
<function main at 0x103528488>
@@ -94,7 +102,7 @@ The ``group`` and ``name`` are arbitrary values defined by the package author
94102
and usually a client will wish to resolve all entry points for a particular
95103
group. Read `the setuptools docs
96104
<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_
97-
for more information on entrypoints, their definition, and usage.
105+
for more information on entry points, their definition, and usage.
98106

99107

100108
.. _metadata:
@@ -235,7 +243,7 @@ method::
235243
"""
236244

237245
The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
238-
properties indicating the path to search and names to match and may
246+
properties indicating the path to search and name to match and may
239247
supply other relevant context.
240248

241249
What this means in practice is that to support finding distribution package

‎Lib/importlib/metadata.py‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ def load(self):
7878
attrs = filter(None, (match.group('attr') or '').split('.'))
7979
return functools.reduce(getattr, attrs, module)
8080

81+
@property
82+
def module(self):
83+
match = self.pattern.match(self.value)
84+
return match.group('module')
85+
86+
@property
87+
def attr(self):
88+
match = self.pattern.match(self.value)
89+
return match.group('attr')
90+
8191
@property
8292
def extras(self):
8393
match = self.pattern.match(self.value)
@@ -170,7 +180,7 @@ def from_name(cls, name):
170180
"""
171181
for resolver in cls._discover_resolvers():
172182
dists = resolver(DistributionFinder.Context(name=name))
173-
dist = next(dists, None)
183+
dist = next(iter(dists), None)
174184
if dist is not None:
175185
return dist
176186
else:
@@ -213,6 +223,17 @@ def _discover_resolvers():
213223
)
214224
return filter(None, declared)
215225

226+
@classmethod
227+
def _local(cls, root='.'):
228+
from pep517 import build, meta
229+
system = build.compat_system(root)
230+
builder = functools.partial(
231+
meta.build,
232+
source_dir=root,
233+
system=system,
234+
)
235+
return PathDistribution(zipfile.Path(meta.build_as_zip(builder)))
236+
216237
@property
217238
def metadata(self):
218239
"""Return the parsed metadata for this Distribution.
@@ -391,7 +412,7 @@ class FastPath:
391412

392413
def __init__(self, root):
393414
self.root = root
394-
self.base = os.path.basename(root).lower()
415+
self.base = os.path.basename(self.root).lower()
395416

396417
def joinpath(self, child):
397418
return pathlib.Path(self.root, child)
@@ -408,8 +429,8 @@ def zip_children(self):
408429
names = zip_path.root.namelist()
409430
self.joinpath = zip_path.joinpath
410431

411-
return (
412-
posixpath.split(child)[0]
432+
return dict.fromkeys(
433+
child.split(posixpath.sep, 1)[0]
413434
for child in names
414435
)
415436

@@ -475,7 +496,6 @@ def _search_paths(cls, name, paths):
475496
)
476497

477498

478-
479499
class PathDistribution(Distribution):
480500
def __init__(self, path):
481501
"""Construct a distribution from a path to the metadata directory.

‎Lib/test/test_importlib/fixtures.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,21 @@ def setUp(self):
161161
build_files(EggInfoFile.files, prefix=self.site_dir)
162162

163163

164+
class LocalPackage:
165+
files = {
166+
"setup.py": """
167+
import setuptools
168+
setuptools.setup(name="local-pkg", version="2.0.1")
169+
""",
170+
}
171+
172+
def setUp(self):
173+
self.fixtures = contextlib.ExitStack()
174+
self.addCleanup(self.fixtures.close)
175+
self.fixtures.enter_context(tempdir_as_cwd())
176+
build_files(self.files)
177+
178+
164179
def build_files(file_defs, prefix=pathlib.Path()):
165180
"""Build a set of files/directories, as described by the
166181

‎Lib/test/test_importlib/test_main.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,19 @@ def test_json_dump(self):
246246
"""
247247
with self.assertRaises(Exception):
248248
json.dumps(self.ep)
249+
250+
def test_module(self):
251+
assert self.ep.module == 'value'
252+
253+
def test_attr(self):
254+
assert self.ep.attr is None
255+
256+
257+
class FileSystem(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
258+
def test_unicode_dir_on_sys_path(self):
259+
"""
260+
Ensure a Unicode subdirectory of a directory on sys.path
261+
does not crash.
262+
"""
263+
fixtures.build_files({'☃': {}}, prefix=self.site_dir)
264+
list(distributions())

‎Lib/test/test_importlib/test_zip.py‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
from contextlib import ExitStack
55
from importlib.metadata import (
6-
distribution, entry_points, files, PackageNotFoundError, version,
6+
distribution, entry_points, files, PackageNotFoundError,
7+
version, distributions,
78
)
8-
from importlib.resources import path
9+
from importlib import resources
910

1011
from test.support import requires_zlib
1112

@@ -14,15 +15,19 @@
1415
class TestZip(unittest.TestCase):
1516
root = 'test.test_importlib.data'
1617

18+
def _fixture_on_path(self, filename):
19+
pkg_file = resources.files(self.root).joinpath(filename)
20+
file = self.resources.enter_context(resources.as_file(pkg_file))
21+
assert file.name.startswith('example-'), file.name
22+
sys.path.insert(0, str(file))
23+
self.resources.callback(sys.path.pop, 0)
24+
1725
def setUp(self):
1826
# Find the path to the example-*.whl so we can add it to the front of
1927
# sys.path, where we'll then try to find the metadata thereof.
2028
self.resources = ExitStack()
2129
self.addCleanup(self.resources.close)
22-
wheel = self.resources.enter_context(
23-
path(self.root, 'example-21.12-py3-none-any.whl'))
24-
sys.path.insert(0, str(wheel))
25-
self.resources.callback(sys.path.pop, 0)
30+
self._fixture_on_path('example-21.12-py3-none-any.whl')
2631

2732
def test_zip_version(self):
2833
self.assertEqual(version('example'), '21.12')
@@ -49,6 +54,10 @@ def test_files(self):
4954
path = str(file.dist.locate_file(file))
5055
assert '.whl/' in path, path
5156

57+
def test_one_distribution(self):
58+
dists = list(distributions(path=sys.path[:1]))
59+
assert len(dists) == 1
60+
5261

5362
@requires_zlib()
5463
class TestEgg(TestZip):
@@ -57,10 +66,7 @@ def setUp(self):
5766
# sys.path, where we'll then try to find the metadata thereof.
5867
self.resources = ExitStack()
5968
self.addCleanup(self.resources.close)
60-
egg = self.resources.enter_context(
61-
path(self.root, 'example-21.12-py3.6.egg'))
62-
sys.path.insert(0, str(egg))
63-
self.resources.callback(sys.path.pop, 0)
69+
self._fixture_on_path('example-21.12-py3.6.egg')
6470

6571
def test_files(self):
6672
for file in files('example'):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refresh importlib.metadata from importlib_metadata 1.6.1.

0 commit comments

Comments
 (0)