Updated importlib-metadata to 2.1.3

This commit is contained in:
Labrys of Knossos 2022-11-29 01:35:03 -05:00
commit f1624a586f
17 changed files with 154 additions and 64 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,3 +1,5 @@
from __future__ import unicode_literals, absolute_import
import io import io
import os import os
import re import re
@ -5,8 +7,6 @@ import abc
import csv import csv
import sys import sys
import zipp import zipp
import email
import pathlib
import operator import operator
import functools import functools
import itertools import itertools
@ -14,18 +14,30 @@ import posixpath
import collections import collections
from ._compat import ( from ._compat import (
NullFinder,
PyPy_repr,
install, install,
) NullFinder,
ConfigParser,
from configparser import ConfigParser suppress,
from contextlib import suppress map,
FileNotFoundError,
IsADirectoryError,
NotADirectoryError,
PermissionError,
pathlib,
ModuleNotFoundError,
MetaPathFinder,
email_message_from_string,
PyPy_repr,
unique_ordered,
str,
)
from importlib import import_module from importlib import import_module
from importlib.abc import MetaPathFinder
from itertools import starmap from itertools import starmap
__metaclass__ = type
__all__ = [ __all__ = [
'Distribution', 'Distribution',
'DistributionFinder', 'DistributionFinder',
@ -37,7 +49,7 @@ __all__ = [
'metadata', 'metadata',
'requires', 'requires',
'version', 'version',
] ]
class PackageNotFoundError(ModuleNotFoundError): class PackageNotFoundError(ModuleNotFoundError):
@ -49,13 +61,13 @@ class PackageNotFoundError(ModuleNotFoundError):
@property @property
def name(self): def name(self):
(name,) = self.args name, = self.args
return name return name
class EntryPoint( class EntryPoint(
PyPy_repr, collections.namedtuple('EntryPointBase', 'name value group') PyPy_repr,
): collections.namedtuple('EntryPointBase', 'name value group')):
"""An entry point as defined by Python packaging conventions. """An entry point as defined by Python packaging conventions.
See `the packaging docs on entry points See `the packaging docs on entry points
@ -65,8 +77,8 @@ class EntryPoint(
pattern = re.compile( pattern = re.compile(
r'(?P<module>[\w.]+)\s*' r'(?P<module>[\w.]+)\s*'
r'(:\s*(?P<attr>[\w.]+))?\s*' r'(:\s*(?P<attr>[\w.]+)\s*)?'
r'(?P<extras>\[.*\])?\s*$' r'((?P<extras>\[.*\])\s*)?$'
) )
""" """
A regular expression describing the syntax for an entry point, A regular expression describing the syntax for an entry point,
@ -217,7 +229,8 @@ class Distribution:
raise ValueError("cannot accept context and kwargs") raise ValueError("cannot accept context and kwargs")
context = context or DistributionFinder.Context(**kwargs) context = context or DistributionFinder.Context(**kwargs)
return itertools.chain.from_iterable( return itertools.chain.from_iterable(
resolver(context) for resolver in cls._discover_resolvers() resolver(context)
for resolver in cls._discover_resolvers()
) )
@staticmethod @staticmethod
@ -233,14 +246,14 @@ class Distribution:
def _discover_resolvers(): def _discover_resolvers():
"""Search the meta_path for resolvers.""" """Search the meta_path for resolvers."""
declared = ( declared = (
getattr(finder, 'find_distributions', None) for finder in sys.meta_path getattr(finder, 'find_distributions', None)
for finder in sys.meta_path
) )
return filter(None, declared) return filter(None, declared)
@classmethod @classmethod
def _local(cls, root='.'): def _local(cls, root='.'):
from pep517 import build, meta from pep517 import build, meta
system = build.compat_system(root) system = build.compat_system(root)
builder = functools.partial( builder = functools.partial(
meta.build, meta.build,
@ -264,7 +277,7 @@ class Distribution:
# (which points to the egg-info file) attribute unchanged. # (which points to the egg-info file) attribute unchanged.
or self.read_text('') or self.read_text('')
) )
return email.message_from_string(text) return email_message_from_string(text)
@property @property
def version(self): def version(self):
@ -330,9 +343,8 @@ class Distribution:
section_pairs = cls._read_sections(source.splitlines()) section_pairs = cls._read_sections(source.splitlines())
sections = { sections = {
section: list(map(operator.itemgetter('line'), results)) section: list(map(operator.itemgetter('line'), results))
for section, results in itertools.groupby( for section, results in
section_pairs, operator.itemgetter('section') itertools.groupby(section_pairs, operator.itemgetter('section'))
)
} }
return cls._convert_egg_info_reqs_to_simple_reqs(sections) return cls._convert_egg_info_reqs_to_simple_reqs(sections)
@ -357,7 +369,6 @@ class Distribution:
requirement. This method converts the former to the requirement. This method converts the former to the
latter. See _test_deps_from_requires_text for an example. latter. See _test_deps_from_requires_text for an example.
""" """
def make_condition(name): def make_condition(name):
return name and 'extra == "{name}"'.format(name=name) return name and 'extra == "{name}"'.format(name=name)
@ -436,7 +447,7 @@ class FastPath:
def children(self): def children(self):
with suppress(Exception): with suppress(Exception):
return os.listdir(self.root or '') return os.listdir(self.root or '.')
with suppress(Exception): with suppress(Exception):
return self.zip_children() return self.zip_children()
return [] return []
@ -446,7 +457,10 @@ class FastPath:
names = zip_path.root.namelist() names = zip_path.root.namelist()
self.joinpath = zip_path.joinpath self.joinpath = zip_path.joinpath
return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) return unique_ordered(
child.split(posixpath.sep, 1)[0]
for child in names
)
def search(self, name): def search(self, name):
return ( return (
@ -460,7 +474,6 @@ class Prepared:
""" """
A prepared search for metadata on a possibly-named package. A prepared search for metadata on a possibly-named package.
""" """
normalized = None normalized = None
suffixes = '.dist-info', '.egg-info' suffixes = '.dist-info', '.egg-info'
exact_matches = [''][:0] exact_matches = [''][:0]
@ -470,7 +483,8 @@ class Prepared:
if name is None: if name is None:
return return
self.normalized = self.normalize(name) self.normalized = self.normalize(name)
self.exact_matches = [self.normalized + suffix for suffix in self.suffixes] self.exact_matches = [
self.normalized + suffix for suffix in self.suffixes]
@staticmethod @staticmethod
def normalize(name): def normalize(name):
@ -493,11 +507,12 @@ class Prepared:
name, sep, rest = pre.partition('-') name, sep, rest = pre.partition('-')
return ( return (
low in self.exact_matches low in self.exact_matches
or ext in self.suffixes or ext in self.suffixes and (
and (not self.normalized or name.replace('.', '_') == self.normalized) not self.normalized or
name.replace('.', '_') == self.normalized
)
# legacy case: # legacy case:
or self.is_egg(base) or self.is_egg(base) and low == 'egg-info'
and low == 'egg-info'
) )
def is_egg(self, base): def is_egg(self, base):
@ -507,8 +522,7 @@ class Prepared:
return ( return (
base == versionless_egg_name base == versionless_egg_name
or base.startswith(prefix) or base.startswith(prefix)
and base.endswith('.egg') and base.endswith('.egg'))
)
@install @install
@ -535,7 +549,8 @@ class MetadataPathFinder(NullFinder, DistributionFinder):
def _search_paths(cls, name, paths): def _search_paths(cls, name, paths):
"""Find metadata directories in paths heuristically.""" """Find metadata directories in paths heuristically."""
return itertools.chain.from_iterable( return itertools.chain.from_iterable(
path.search(Prepared(name)) for path in map(FastPath, paths) path.search(Prepared(name))
for path in map(FastPath, paths)
) )
@ -549,15 +564,9 @@ class PathDistribution(Distribution):
self._path = path self._path = path
def read_text(self, filename): def read_text(self, filename):
with suppress( with suppress(FileNotFoundError, IsADirectoryError, KeyError,
FileNotFoundError, NotADirectoryError, PermissionError):
IsADirectoryError,
KeyError,
NotADirectoryError,
PermissionError,
):
return self._path.joinpath(filename).read_text(encoding='utf-8') return self._path.joinpath(filename).read_text(encoding='utf-8')
read_text.__doc__ = Distribution.read_text.__doc__ read_text.__doc__ = Distribution.read_text.__doc__
def locate_file(self, path): def locate_file(self, path):
@ -605,11 +614,15 @@ def entry_points():
:return: EntryPoint objects for all installed packages. :return: EntryPoint objects for all installed packages.
""" """
eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions()) eps = itertools.chain.from_iterable(
dist.entry_points for dist in distributions())
by_group = operator.attrgetter('group') by_group = operator.attrgetter('group')
ordered = sorted(eps, key=by_group) ordered = sorted(eps, key=by_group)
grouped = itertools.groupby(ordered, by_group) grouped = itertools.groupby(ordered, by_group)
return {group: tuple(eps) for group, eps in grouped} return {
group: tuple(eps)
for group, eps in grouped
}
def files(distribution_name): def files(distribution_name):

View file

@ -1,7 +1,59 @@
from __future__ import absolute_import, unicode_literals
import io
import abc
import sys import sys
import email
__all__ = ['install', 'NullFinder', 'PyPy_repr'] if sys.version_info > (3,): # pragma: nocover
import builtins
from configparser import ConfigParser
import contextlib
FileNotFoundError = builtins.FileNotFoundError
IsADirectoryError = builtins.IsADirectoryError
NotADirectoryError = builtins.NotADirectoryError
PermissionError = builtins.PermissionError
map = builtins.map
from itertools import filterfalse
else: # pragma: nocover
from backports.configparser import ConfigParser
from itertools import imap as map # type: ignore
from itertools import ifilterfalse as filterfalse
import contextlib2 as contextlib
FileNotFoundError = IOError, OSError
IsADirectoryError = IOError, OSError
NotADirectoryError = IOError, OSError
PermissionError = IOError, OSError
str = type('')
suppress = contextlib.suppress
if sys.version_info > (3, 5): # pragma: nocover
import pathlib
else: # pragma: nocover
import pathlib2 as pathlib
try:
ModuleNotFoundError = builtins.FileNotFoundError
except (NameError, AttributeError): # pragma: nocover
ModuleNotFoundError = ImportError # type: ignore
if sys.version_info >= (3,): # pragma: nocover
from importlib.abc import MetaPathFinder
else: # pragma: nocover
class MetaPathFinder(object):
__metaclass__ = abc.ABCMeta
__metaclass__ = type
__all__ = [
'install', 'NullFinder', 'MetaPathFinder', 'ModuleNotFoundError',
'pathlib', 'ConfigParser', 'map', 'suppress', 'FileNotFoundError',
'NotADirectoryError', 'email_message_from_string',
]
def install(cls): def install(cls):
@ -25,12 +77,11 @@ def disable_stdlib_finder():
See #91 for more background for rationale on this sketchy See #91 for more background for rationale on this sketchy
behavior. behavior.
""" """
def matches(finder): def matches(finder):
return getattr( return (
finder, '__module__', None getattr(finder, '__module__', None) == '_frozen_importlib_external'
) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') and hasattr(finder, 'find_distributions')
)
for finder in filter(matches, sys.meta_path): # pragma: nocover for finder in filter(matches, sys.meta_path): # pragma: nocover
del finder.find_distributions del finder.find_distributions
@ -40,7 +91,6 @@ class NullFinder:
A "Finder" (aka "MetaClassFinder") that never finds any modules, A "Finder" (aka "MetaClassFinder") that never finds any modules,
but may find distributions. but may find distributions.
""" """
@staticmethod @staticmethod
def find_spec(*args, **kwargs): def find_spec(*args, **kwargs):
return None return None
@ -54,22 +104,49 @@ class NullFinder:
find_module = find_spec find_module = find_spec
def py2_message_from_string(text): # nocoverpy3
# Work around https://bugs.python.org/issue25545 where
# email.message_from_string cannot handle Unicode on Python 2.
io_buffer = io.StringIO(text)
return email.message_from_file(io_buffer)
email_message_from_string = (
py2_message_from_string
if sys.version_info < (3,) else
email.message_from_string
)
class PyPy_repr: class PyPy_repr:
""" """
Override repr for EntryPoint objects on PyPy to avoid __iter__ access. Override repr for EntryPoint objects on PyPy to avoid __iter__ access.
Ref #97, #102. Ref #97, #102.
""" """
affected = hasattr(sys, 'pypy_version_info') affected = hasattr(sys, 'pypy_version_info')
def __compat_repr__(self): # pragma: nocover def __compat_repr__(self): # pragma: nocover
def make_param(name): def make_param(name):
value = getattr(self, name) value = getattr(self, name)
return '{name}={value!r}'.format(**locals()) return '{name}={value!r}'.format(**locals())
params = ', '.join(map(make_param, self._fields)) params = ', '.join(map(make_param, self._fields))
return 'EntryPoint({params})'.format(**locals()) return 'EntryPoint({params})'.format(**locals())
if affected: # pragma: nocover if affected: # pragma: nocover
__repr__ = __compat_repr__ __repr__ = __compat_repr__
del affected del affected
# from itertools recipes
def unique_everseen(iterable): # pragma: nocover
"List unique elements, preserving order. Remember all elements ever seen."
seen = set()
seen_add = seen.add
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
unique_ordered = (
unique_everseen if sys.version_info < (3, 7) else dict.fromkeys)