Skip to content

Commit

Permalink
Apply changes as found downstream in Python 3.10.8.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Nov 5, 2022
1 parent ba30e9b commit 9963088
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 14 deletions.
10 changes: 4 additions & 6 deletions Doc/library/importlib.metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
.. versionchanged:: 3.10
``importlib.metadata`` is no longer provisional.

**Source code:** :source:`Lib/importlib/metadata.py`
**Source code:** :source:`Lib/importlib/metadata/__init__.py`

``importlib.metadata`` is a library that provides for access to installed
package metadata. Built in part on Python's import system, this library
Expand Down Expand Up @@ -136,7 +136,7 @@ Inspect the resolved entry point::
The ``group`` and ``name`` are arbitrary values defined by the package author
and usually a client will wish to resolve all entry points for a particular
group. Read `the setuptools docs
<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_
<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_
for more information on entry points, their definition, and usage.

*Compatibility Note*
Expand Down Expand Up @@ -255,7 +255,7 @@ function::
Package distributions
---------------------

A convience method to resolve the distribution or
A convenience method to resolve the distribution or
distributions (in the case of a namespace package) for top-level
Python packages or modules::

Expand All @@ -264,6 +264,7 @@ Python packages or modules::

.. versionadded:: 3.10

.. _distributions:

Distributions
=============
Expand Down Expand Up @@ -335,6 +336,3 @@ a custom finder, return instances of this derived ``Distribution`` in the
.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api
.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html


.. rubric:: Footnotes
54 changes: 46 additions & 8 deletions Lib/importlib/metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,21 @@ class EntryPoint(
See `the packaging docs on entry points
<https://packaging.python.org/specifications/entry-points/>`_
for more information.
>>> ep = EntryPoint(
... name=None, group=None, value='package.module:attr [extra1, extra2]')
>>> ep.module
'package.module'
>>> ep.attr
'attr'
>>> ep.extras
['extra1', 'extra2']
"""

pattern = re.compile(
r'(?P<module>[\w.]+)\s*'
r'(:\s*(?P<attr>[\w.]+))?\s*'
r'(?P<extras>\[.*\])?\s*$'
r'(:\s*(?P<attr>[\w.]+)\s*)?'
r'((?P<extras>\[.*\])\s*)?$'
)
"""
A regular expression describing the syntax for an entry point,
Expand Down Expand Up @@ -176,7 +185,7 @@ def attr(self):
@property
def extras(self):
match = self.pattern.match(self.value)
return list(re.finditer(r'\w+', match.group('extras') or ''))
return re.findall(r'\w+', match.group('extras') or '')

def _for(self, dist):
self.dist = dist
Expand All @@ -200,6 +209,25 @@ def __reduce__(self):
)

def matches(self, **params):
"""
EntryPoint matches the given parameters.
>>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]')
>>> ep.matches(group='foo')
True
>>> ep.matches(name='bar', value='bing:bong [extra1, extra2]')
True
>>> ep.matches(group='foo', name='other')
False
>>> ep.matches()
True
>>> ep.matches(extras=['extra1', 'extra2'])
True
>>> ep.matches(module='bing')
True
>>> ep.matches(attr='bong')
True
"""
attrs = (getattr(self, param) for param in params)
return all(map(operator.eq, params.values(), attrs))

Expand Down Expand Up @@ -236,6 +264,8 @@ class DeprecatedList(list):
1
"""

__slots__ = ()

_warn = functools.partial(
warnings.warn,
"EntryPoints list interface is deprecated. Cast to list if needed.",
Expand Down Expand Up @@ -648,7 +678,7 @@ def _read_dist_info_reqs(self):

def _read_egg_info_reqs(self):
source = self.read_text('requires.txt')
return source and self._deps_from_requires_text(source)
return None if source is None else self._deps_from_requires_text(source)

@classmethod
def _deps_from_requires_text(cls, source):
Expand All @@ -669,16 +699,25 @@ def _convert_egg_info_reqs_to_simple_reqs(sections):
def make_condition(name):
return name and f'extra == "{name}"'

def parse_condition(section):
def quoted_marker(section):
section = section or ''
extra, sep, markers = section.partition(':')
if extra and markers:
markers = f'({markers})'
conditions = list(filter(None, [markers, make_condition(extra)]))
return '; ' + ' and '.join(conditions) if conditions else ''

def url_req_space(req):
"""
PEP 508 requires a space between the url_spec and the quoted_marker.
Ref python/importlib_metadata#357.
"""
# '@' is uniquely indicative of a url_req.
return ' ' * ('@' in req)

for section in sections:
yield section.value + parse_condition(section.name)
space = url_req_space(section.value)
yield section.value + space + quoted_marker(section.name)


class DistributionFinder(MetaPathFinder):
Expand Down Expand Up @@ -741,14 +780,13 @@ def __new__(cls, root):

def __init__(self, root):
self.root = root
self.base = os.path.basename(self.root).lower()

def joinpath(self, child):
return pathlib.Path(self.root, child)

def children(self):
with suppress(Exception):
return os.listdir(self.root or '')
return os.listdir(self.root or '.')
with suppress(Exception):
return self.zip_children()
return []
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_importlib/test_metadata_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ def test_entry_points_groups_get(self):
entry_points().get('entries', 'default') == entry_points()['entries']
entry_points().get('missing', ()) == ()

def test_entry_points_allows_no_attributes(self):
ep = entry_points().select(group='entries', name='main')
with self.assertRaises(AttributeError):
ep.foo = 4

def test_metadata_for_this_package(self):
md = metadata('egginfo-pkg')
assert md['author'] == 'Steven Ma'
Expand Down Expand Up @@ -217,6 +222,16 @@ def test_requires_egg_info(self):
assert len(deps) == 2
assert any(dep == 'wheel >= 1.0; python_version >= "2.7"' for dep in deps)

def test_requires_egg_info_empty(self):
fixtures.build_files(
{
'requires.txt': '',
},
self.site_dir.joinpath('egginfo_pkg.egg-info'),
)
deps = requires('egginfo-pkg')
assert deps == []

def test_requires_dist_info(self):
deps = requires('distinfo-pkg')
assert len(deps) == 2
Expand All @@ -235,6 +250,7 @@ def test_more_complex_deps_requires_text(self):
[extra1]
dep4
dep6@ git+https://example.com/python/dep.git@v1.0.0
[extra2:python_version < "3"]
dep5
Expand All @@ -247,6 +263,7 @@ def test_more_complex_deps_requires_text(self):
'dep3; python_version < "3"',
'dep4; extra == "extra1"',
'dep5; (python_version < "3") and extra == "extra2"',
'dep6@ git+https://example.com/python/dep.git@v1.0.0 ; extra == "extra1"',
]
# It's important that the environment marker expression be
# wrapped in parentheses to avoid the following 'and' binding more
Expand Down

0 comments on commit 9963088

Please sign in to comment.