summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Lib/importlib
diff options
context:
space:
mode:
authorshadchin <[email protected]>2022-02-10 16:44:39 +0300
committerDaniil Cherednik <[email protected]>2022-02-10 16:44:39 +0300
commite9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch)
tree64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/tools/python3/src/Lib/importlib
parent2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff)
Restoring authorship annotation for <[email protected]>. Commit 2 of 2.
Diffstat (limited to 'contrib/tools/python3/src/Lib/importlib')
-rw-r--r--contrib/tools/python3/src/Lib/importlib/__init__.py4
-rw-r--r--contrib/tools/python3/src/Lib/importlib/_bootstrap.py234
-rw-r--r--contrib/tools/python3/src/Lib/importlib/_bootstrap_external.py418
-rw-r--r--contrib/tools/python3/src/Lib/importlib/_common.py124
-rw-r--r--contrib/tools/python3/src/Lib/importlib/abc.py174
-rw-r--r--contrib/tools/python3/src/Lib/importlib/metadata.py1190
-rw-r--r--contrib/tools/python3/src/Lib/importlib/resources.py118
-rw-r--r--contrib/tools/python3/src/Lib/importlib/util.py4
8 files changed, 1133 insertions, 1133 deletions
diff --git a/contrib/tools/python3/src/Lib/importlib/__init__.py b/contrib/tools/python3/src/Lib/importlib/__init__.py
index feaba8010de..0c73c505f98 100644
--- a/contrib/tools/python3/src/Lib/importlib/__init__.py
+++ b/contrib/tools/python3/src/Lib/importlib/__init__.py
@@ -48,8 +48,8 @@ else:
sys.modules['importlib._bootstrap_external'] = _bootstrap_external
# To simplify imports in test code
-_pack_uint32 = _bootstrap_external._pack_uint32
-_unpack_uint32 = _bootstrap_external._unpack_uint32
+_pack_uint32 = _bootstrap_external._pack_uint32
+_unpack_uint32 = _bootstrap_external._unpack_uint32
# Fully bootstrapped at this point, import whatever you like, circular
# dependencies and startup overhead minimisation permitting :)
diff --git a/contrib/tools/python3/src/Lib/importlib/_bootstrap.py b/contrib/tools/python3/src/Lib/importlib/_bootstrap.py
index 4a2553175ce..e00b27ece26 100644
--- a/contrib/tools/python3/src/Lib/importlib/_bootstrap.py
+++ b/contrib/tools/python3/src/Lib/importlib/_bootstrap.py
@@ -7,9 +7,9 @@ work. One should use importlib as the public-facing version of this module.
"""
#
-# IMPORTANT: Whenever making changes to this module, be sure to run a top-level
-# `make regen-importlib` followed by `make` in order to get the frozen version
-# of the module updated. Not doing so will result in the Makefile to fail for
+# IMPORTANT: Whenever making changes to this module, be sure to run a top-level
+# `make regen-importlib` followed by `make` in order to get the frozen version
+# of the module updated. Not doing so will result in the Makefile to fail for
# all others who don't have a ./python around to freeze the module
# in the early stages of compilation.
#
@@ -67,7 +67,7 @@ class _ModuleLock:
# Deadlock avoidance for concurrent circular imports.
me = _thread.get_ident()
tid = self.owner
- seen = set()
+ seen = set()
while True:
lock = _blocking_on.get(tid)
if lock is None:
@@ -75,14 +75,14 @@ class _ModuleLock:
tid = lock.owner
if tid == me:
return True
- if tid in seen:
- # bpo 38091: the chain of tid's we encounter here
- # eventually leads to a fixpoint or a cycle, but
- # does not reach 'me'. This means we would not
- # actually deadlock. This can happen if other
- # threads are at the beginning of acquire() below.
- return False
- seen.add(tid)
+ if tid in seen:
+ # bpo 38091: the chain of tid's we encounter here
+ # eventually leads to a fixpoint or a cycle, but
+ # does not reach 'me'. This means we would not
+ # actually deadlock. This can happen if other
+ # threads are at the beginning of acquire() below.
+ return False
+ seen.add(tid)
def acquire(self):
"""
@@ -380,7 +380,7 @@ class ModuleSpec:
self.cached == other.cached and
self.has_location == other.has_location)
except AttributeError:
- return NotImplemented
+ return NotImplemented
@property
def cached(self):
@@ -596,44 +596,44 @@ def _exec(spec, module):
if sys.modules.get(name) is not module:
msg = 'module {!r} not in sys.modules'.format(name)
raise ImportError(msg, name=name)
- try:
- if spec.loader is None:
- if spec.submodule_search_locations is None:
- raise ImportError('missing loader', name=spec.name)
- # Namespace package.
- _init_module_attrs(spec, module, override=True)
- else:
- _init_module_attrs(spec, module, override=True)
- if not hasattr(spec.loader, 'exec_module'):
- # (issue19713) Once BuiltinImporter and ExtensionFileLoader
- # have exec_module() implemented, we can add a deprecation
- # warning here.
- spec.loader.load_module(name)
- else:
- spec.loader.exec_module(module)
- finally:
- # Update the order of insertion into sys.modules for module
- # clean-up at shutdown.
- module = sys.modules.pop(spec.name)
- sys.modules[spec.name] = module
- return module
+ try:
+ if spec.loader is None:
+ if spec.submodule_search_locations is None:
+ raise ImportError('missing loader', name=spec.name)
+ # Namespace package.
+ _init_module_attrs(spec, module, override=True)
+ else:
+ _init_module_attrs(spec, module, override=True)
+ if not hasattr(spec.loader, 'exec_module'):
+ # (issue19713) Once BuiltinImporter and ExtensionFileLoader
+ # have exec_module() implemented, we can add a deprecation
+ # warning here.
+ spec.loader.load_module(name)
+ else:
+ spec.loader.exec_module(module)
+ finally:
+ # Update the order of insertion into sys.modules for module
+ # clean-up at shutdown.
+ module = sys.modules.pop(spec.name)
+ sys.modules[spec.name] = module
+ return module
def _load_backward_compatible(spec):
# (issue19713) Once BuiltinImporter and ExtensionFileLoader
# have exec_module() implemented, we can add a deprecation
# warning here.
- try:
- spec.loader.load_module(spec.name)
- except:
- if spec.name in sys.modules:
- module = sys.modules.pop(spec.name)
- sys.modules[spec.name] = module
- raise
+ try:
+ spec.loader.load_module(spec.name)
+ except:
+ if spec.name in sys.modules:
+ module = sys.modules.pop(spec.name)
+ sys.modules[spec.name] = module
+ raise
# The module must be in sys.modules at this point!
- # Move it to the end of sys.modules.
- module = sys.modules.pop(spec.name)
- sys.modules[spec.name] = module
+ # Move it to the end of sys.modules.
+ module = sys.modules.pop(spec.name)
+ sys.modules[spec.name] = module
if getattr(module, '__loader__', None) is None:
try:
module.__loader__ = spec.loader
@@ -659,43 +659,43 @@ def _load_backward_compatible(spec):
def _load_unlocked(spec):
# A helper for direct use by the import system.
if spec.loader is not None:
- # Not a namespace package.
+ # Not a namespace package.
if not hasattr(spec.loader, 'exec_module'):
return _load_backward_compatible(spec)
module = module_from_spec(spec)
- # This must be done before putting the module in sys.modules
- # (otherwise an optimization shortcut in import.c becomes
- # wrong).
- spec._initializing = True
- try:
- sys.modules[spec.name] = module
- try:
- if spec.loader is None:
- if spec.submodule_search_locations is None:
- raise ImportError('missing loader', name=spec.name)
- # A namespace package so do nothing.
- else:
- spec.loader.exec_module(module)
- except:
- try:
- del sys.modules[spec.name]
- except KeyError:
- pass
- raise
- # Move the module to the end of sys.modules.
- # We don't ensure that the import-related module attributes get
- # set in the sys.modules replacement case. Such modules are on
- # their own.
- module = sys.modules.pop(spec.name)
- sys.modules[spec.name] = module
- _verbose_message('import {!r} # {!r}', spec.name, spec.loader)
- finally:
- spec._initializing = False
+ # This must be done before putting the module in sys.modules
+ # (otherwise an optimization shortcut in import.c becomes
+ # wrong).
+ spec._initializing = True
+ try:
+ sys.modules[spec.name] = module
+ try:
+ if spec.loader is None:
+ if spec.submodule_search_locations is None:
+ raise ImportError('missing loader', name=spec.name)
+ # A namespace package so do nothing.
+ else:
+ spec.loader.exec_module(module)
+ except:
+ try:
+ del sys.modules[spec.name]
+ except KeyError:
+ pass
+ raise
+ # Move the module to the end of sys.modules.
+ # We don't ensure that the import-related module attributes get
+ # set in the sys.modules replacement case. Such modules are on
+ # their own.
+ module = sys.modules.pop(spec.name)
+ sys.modules[spec.name] = module
+ _verbose_message('import {!r} # {!r}', spec.name, spec.loader)
+ finally:
+ spec._initializing = False
+
+ return module
- return module
-
# A method used during testing of _load_unlocked() and by
# _load_module_shim().
def _load(spec):
@@ -722,8 +722,8 @@ class BuiltinImporter:
"""
- _ORIGIN = "built-in"
-
+ _ORIGIN = "built-in"
+
@staticmethod
def module_repr(module):
"""Return repr for the module.
@@ -731,14 +731,14 @@ class BuiltinImporter:
The method is deprecated. The import machinery does the job itself.
"""
- return f'<module {module.__name__!r} ({BuiltinImporter._ORIGIN})>'
+ return f'<module {module.__name__!r} ({BuiltinImporter._ORIGIN})>'
@classmethod
def find_spec(cls, fullname, path=None, target=None):
if path is not None:
return None
if _imp.is_builtin(fullname):
- return spec_from_loader(fullname, cls, origin=cls._ORIGIN)
+ return spec_from_loader(fullname, cls, origin=cls._ORIGIN)
else:
return None
@@ -797,8 +797,8 @@ class FrozenImporter:
"""
- _ORIGIN = "frozen"
-
+ _ORIGIN = "frozen"
+
@staticmethod
def module_repr(m):
"""Return repr for the module.
@@ -806,12 +806,12 @@ class FrozenImporter:
The method is deprecated. The import machinery does the job itself.
"""
- return '<module {!r} ({})>'.format(m.__name__, FrozenImporter._ORIGIN)
+ return '<module {!r} ({})>'.format(m.__name__, FrozenImporter._ORIGIN)
@classmethod
def find_spec(cls, fullname, path=None, target=None):
if _imp.is_frozen(fullname):
- return spec_from_loader(fullname, cls, origin=cls._ORIGIN)
+ return spec_from_loader(fullname, cls, origin=cls._ORIGIN)
else:
return None
@@ -884,7 +884,7 @@ def _resolve_name(name, package, level):
"""Resolve a relative module name to an absolute one."""
bits = package.rsplit('.', level - 1)
if len(bits) < level:
- raise ImportError('attempted relative import beyond top-level package')
+ raise ImportError('attempted relative import beyond top-level package')
base = bits[0]
return '{}.{}'.format(base, name) if name else base
@@ -987,12 +987,12 @@ def _find_and_load_unlocked(name, import_):
if parent:
# Set the module as an attribute on its parent.
parent_module = sys.modules[parent]
- child = name.rpartition('.')[2]
- try:
- setattr(parent_module, child, module)
- except AttributeError:
- msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
- _warnings.warn(msg, ImportWarning)
+ child = name.rpartition('.')[2]
+ try:
+ setattr(parent_module, child, module)
+ except AttributeError:
+ msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
+ _warnings.warn(msg, ImportWarning)
return module
@@ -1040,30 +1040,30 @@ def _handle_fromlist(module, fromlist, import_, *, recursive=False):
"""
# The hell that is fromlist ...
# If a package was imported, try to import stuff from fromlist.
- for x in fromlist:
- if not isinstance(x, str):
- if recursive:
- where = module.__name__ + '.__all__'
- else:
- where = "``from list''"
- raise TypeError(f"Item in {where} must be str, "
- f"not {type(x).__name__}")
- elif x == '*':
- if not recursive and hasattr(module, '__all__'):
- _handle_fromlist(module, module.__all__, import_,
- recursive=True)
- elif not hasattr(module, x):
- from_name = '{}.{}'.format(module.__name__, x)
- try:
- _call_with_frames_removed(import_, from_name)
- except ModuleNotFoundError as exc:
- # Backwards-compatibility dictates we ignore failed
- # imports triggered by fromlist for modules that don't
- # exist.
- if (exc.name == from_name and
- sys.modules.get(from_name, _NEEDS_LOADING) is not None):
- continue
- raise
+ for x in fromlist:
+ if not isinstance(x, str):
+ if recursive:
+ where = module.__name__ + '.__all__'
+ else:
+ where = "``from list''"
+ raise TypeError(f"Item in {where} must be str, "
+ f"not {type(x).__name__}")
+ elif x == '*':
+ if not recursive and hasattr(module, '__all__'):
+ _handle_fromlist(module, module.__all__, import_,
+ recursive=True)
+ elif not hasattr(module, x):
+ from_name = '{}.{}'.format(module.__name__, x)
+ try:
+ _call_with_frames_removed(import_, from_name)
+ except ModuleNotFoundError as exc:
+ # Backwards-compatibility dictates we ignore failed
+ # imports triggered by fromlist for modules that don't
+ # exist.
+ if (exc.name == from_name and
+ sys.modules.get(from_name, _NEEDS_LOADING) is not None):
+ continue
+ raise
return module
@@ -1125,10 +1125,10 @@ def __import__(name, globals=None, locals=None, fromlist=(), level=0):
# Slice end needs to be positive to alleviate need to special-case
# when ``'.' not in name``.
return sys.modules[module.__name__[:len(module.__name__)-cut_off]]
- elif hasattr(module, '__path__'):
- return _handle_fromlist(module, fromlist, _gcd_import)
+ elif hasattr(module, '__path__'):
+ return _handle_fromlist(module, fromlist, _gcd_import)
else:
- return module
+ return module
def _builtin_from_name(name):
diff --git a/contrib/tools/python3/src/Lib/importlib/_bootstrap_external.py b/contrib/tools/python3/src/Lib/importlib/_bootstrap_external.py
index 0a287848379..fe31f437dac 100644
--- a/contrib/tools/python3/src/Lib/importlib/_bootstrap_external.py
+++ b/contrib/tools/python3/src/Lib/importlib/_bootstrap_external.py
@@ -19,34 +19,34 @@ work. One should use importlib as the public-facing version of this module.
# reference any injected objects! This includes not only global code but also
# anything specified at the class level.
-# Import builtin modules
-import _imp
-import _io
-import sys
-import _warnings
-import marshal
-
-
-_MS_WINDOWS = (sys.platform == 'win32')
-if _MS_WINDOWS:
- import nt as _os
- import winreg
-else:
- import posix as _os
-
-
-if _MS_WINDOWS:
- path_separators = ['\\', '/']
-else:
- path_separators = ['/']
-# Assumption made in _path_join()
-assert all(len(sep) == 1 for sep in path_separators)
-path_sep = path_separators[0]
-path_sep_tuple = tuple(path_separators)
-path_separators = ''.join(path_separators)
-_pathseps_with_colon = {f':{s}' for s in path_separators}
-
-
+# Import builtin modules
+import _imp
+import _io
+import sys
+import _warnings
+import marshal
+
+
+_MS_WINDOWS = (sys.platform == 'win32')
+if _MS_WINDOWS:
+ import nt as _os
+ import winreg
+else:
+ import posix as _os
+
+
+if _MS_WINDOWS:
+ path_separators = ['\\', '/']
+else:
+ path_separators = ['/']
+# Assumption made in _path_join()
+assert all(len(sep) == 1 for sep in path_separators)
+path_sep = path_separators[0]
+path_sep_tuple = tuple(path_separators)
+path_separators = ''.join(path_separators)
+_pathseps_with_colon = {f':{s}' for s in path_separators}
+
+
# Bootstrap-related code ######################################################
_CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win',
_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin'
@@ -62,8 +62,8 @@ def _make_relax_case():
key = b'PYTHONCASEOK'
def _relax_case():
- """True if filenames must be checked case-insensitively and ignore environment flags are not set."""
- return not sys.flags.ignore_environment and key in _os.environ
+ """True if filenames must be checked case-insensitively and ignore environment flags are not set."""
+ return not sys.flags.ignore_environment and key in _os.environ
else:
def _relax_case():
"""True if filenames must be checked case-insensitively."""
@@ -71,65 +71,65 @@ def _make_relax_case():
return _relax_case
-def _pack_uint32(x):
+def _pack_uint32(x):
"""Convert a 32-bit integer to little-endian."""
return (int(x) & 0xFFFFFFFF).to_bytes(4, 'little')
-def _unpack_uint32(data):
+def _unpack_uint32(data):
"""Convert 4 bytes in little-endian to an integer."""
- assert len(data) == 4
- return int.from_bytes(data, 'little')
+ assert len(data) == 4
+ return int.from_bytes(data, 'little')
+
+def _unpack_uint16(data):
+ """Convert 2 bytes in little-endian to an integer."""
+ assert len(data) == 2
+ return int.from_bytes(data, 'little')
-def _unpack_uint16(data):
- """Convert 2 bytes in little-endian to an integer."""
- assert len(data) == 2
- return int.from_bytes(data, 'little')
-
-if _MS_WINDOWS:
- def _path_join(*path_parts):
- """Replacement for os.path.join()."""
- if not path_parts:
- return ""
- if len(path_parts) == 1:
- return path_parts[0]
- root = ""
- path = []
- for new_root, tail in map(_os._path_splitroot, path_parts):
- if new_root.startswith(path_sep_tuple) or new_root.endswith(path_sep_tuple):
- root = new_root.rstrip(path_separators) or root
- path = [path_sep + tail]
- elif new_root.endswith(':'):
- if root.casefold() != new_root.casefold():
- # Drive relative paths have to be resolved by the OS, so we reset the
- # tail but do not add a path_sep prefix.
- root = new_root
- path = [tail]
- else:
- path.append(tail)
- else:
- root = new_root or root
- path.append(tail)
- path = [p.rstrip(path_separators) for p in path if p]
- if len(path) == 1 and not path[0]:
- # Avoid losing the root's trailing separator when joining with nothing
- return root + path_sep
- return root + path_sep.join(path)
+if _MS_WINDOWS:
+ def _path_join(*path_parts):
+ """Replacement for os.path.join()."""
+ if not path_parts:
+ return ""
+ if len(path_parts) == 1:
+ return path_parts[0]
+ root = ""
+ path = []
+ for new_root, tail in map(_os._path_splitroot, path_parts):
+ if new_root.startswith(path_sep_tuple) or new_root.endswith(path_sep_tuple):
+ root = new_root.rstrip(path_separators) or root
+ path = [path_sep + tail]
+ elif new_root.endswith(':'):
+ if root.casefold() != new_root.casefold():
+ # Drive relative paths have to be resolved by the OS, so we reset the
+ # tail but do not add a path_sep prefix.
+ root = new_root
+ path = [tail]
+ else:
+ path.append(tail)
+ else:
+ root = new_root or root
+ path.append(tail)
+ path = [p.rstrip(path_separators) for p in path if p]
+ if len(path) == 1 and not path[0]:
+ # Avoid losing the root's trailing separator when joining with nothing
+ return root + path_sep
+ return root + path_sep.join(path)
+
+else:
+ def _path_join(*path_parts):
+ """Replacement for os.path.join()."""
+ return path_sep.join([part.rstrip(path_separators)
+ for part in path_parts if part])
-else:
- def _path_join(*path_parts):
- """Replacement for os.path.join()."""
- return path_sep.join([part.rstrip(path_separators)
- for part in path_parts if part])
-
def _path_split(path):
"""Replacement for os.path.split()."""
- i = max(path.rfind(p) for p in path_separators)
- if i < 0:
- return '', path
- return path[:i], path[i + 1:]
+ i = max(path.rfind(p) for p in path_separators)
+ if i < 0:
+ return '', path
+ return path[:i], path[i + 1:]
def _path_stat(path):
@@ -163,20 +163,20 @@ def _path_isdir(path):
return _path_is_mode_type(path, 0o040000)
-if _MS_WINDOWS:
- def _path_isabs(path):
- """Replacement for os.path.isabs."""
- if not path:
- return False
- root = _os._path_splitroot(path)[0].replace('/', '\\')
- return len(root) > 1 and (root.startswith('\\\\') or root.endswith('\\'))
-
-else:
- def _path_isabs(path):
- """Replacement for os.path.isabs."""
- return path.startswith(path_separators)
-
-
+if _MS_WINDOWS:
+ def _path_isabs(path):
+ """Replacement for os.path.isabs."""
+ if not path:
+ return False
+ root = _os._path_splitroot(path)[0].replace('/', '\\')
+ return len(root) > 1 and (root.startswith('\\\\') or root.endswith('\\'))
+
+else:
+ def _path_isabs(path):
+ """Replacement for os.path.isabs."""
+ return path.startswith(path_separators)
+
+
def _write_atomic(path, data, mode=0o666):
"""Best-effort function to write data to a path atomically.
Be prepared to handle a FileExistsError if concurrent writing of the
@@ -321,23 +321,23 @@ _code_type = type(_write_atomic.__code__)
# Python 3.7a2 3391 (update GET_AITER #31709)
# Python 3.7a4 3392 (PEP 552: Deterministic pycs #31650)
# Python 3.7b1 3393 (remove STORE_ANNOTATION opcode #32550)
-# Python 3.7b5 3394 (restored docstring as the first stmt in the body;
+# Python 3.7b5 3394 (restored docstring as the first stmt in the body;
# this might affected the first line number #32911)
-# Python 3.8a1 3400 (move frame block handling to compiler #17611)
-# Python 3.8a1 3401 (add END_ASYNC_FOR #33041)
-# Python 3.8a1 3410 (PEP570 Python Positional-Only Parameters #36540)
-# Python 3.8b2 3411 (Reverse evaluation order of key: value in dict
-# comprehensions #35224)
-# Python 3.8b2 3412 (Swap the position of positional args and positional
-# only args in ast.arguments #37593)
-# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
-# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
-# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
-# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
-# Python 3.9a2 3423 (add IS_OP, CONTAINS_OP and JUMP_IF_NOT_EXC_MATCH bytecodes #39156)
-# Python 3.9a2 3424 (simplify bytecodes for *value unpacking)
-# Python 3.9a2 3425 (simplify bytecodes for **value unpacking)
-
+# Python 3.8a1 3400 (move frame block handling to compiler #17611)
+# Python 3.8a1 3401 (add END_ASYNC_FOR #33041)
+# Python 3.8a1 3410 (PEP570 Python Positional-Only Parameters #36540)
+# Python 3.8b2 3411 (Reverse evaluation order of key: value in dict
+# comprehensions #35224)
+# Python 3.8b2 3412 (Swap the position of positional args and positional
+# only args in ast.arguments #37593)
+# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
+# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
+# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
+# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
+# Python 3.9a2 3423 (add IS_OP, CONTAINS_OP and JUMP_IF_NOT_EXC_MATCH bytecodes #39156)
+# Python 3.9a2 3424 (simplify bytecodes for *value unpacking)
+# Python 3.9a2 3425 (simplify bytecodes for **value unpacking)
+
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
@@ -346,7 +346,7 @@ _code_type = type(_write_atomic.__code__)
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3425).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3425).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'
@@ -400,35 +400,35 @@ def cache_from_source(path, debug_override=None, *, optimization=None):
if not optimization.isalnum():
raise ValueError('{!r} is not alphanumeric'.format(optimization))
almost_filename = '{}.{}{}'.format(almost_filename, _OPT, optimization)
- filename = almost_filename + BYTECODE_SUFFIXES[0]
- if sys.pycache_prefix is not None:
- # We need an absolute path to the py file to avoid the possibility of
- # collisions within sys.pycache_prefix, if someone has two different
- # `foo/bar.py` on their system and they import both of them using the
- # same sys.pycache_prefix. Let's say sys.pycache_prefix is
- # `C:\Bytecode`; the idea here is that if we get `Foo\Bar`, we first
- # make it absolute (`C:\Somewhere\Foo\Bar`), then make it root-relative
- # (`Somewhere\Foo\Bar`), so we end up placing the bytecode file in an
- # unambiguous `C:\Bytecode\Somewhere\Foo\Bar\`.
- if not _path_isabs(head):
- head = _path_join(_os.getcwd(), head)
+ filename = almost_filename + BYTECODE_SUFFIXES[0]
+ if sys.pycache_prefix is not None:
+ # We need an absolute path to the py file to avoid the possibility of
+ # collisions within sys.pycache_prefix, if someone has two different
+ # `foo/bar.py` on their system and they import both of them using the
+ # same sys.pycache_prefix. Let's say sys.pycache_prefix is
+ # `C:\Bytecode`; the idea here is that if we get `Foo\Bar`, we first
+ # make it absolute (`C:\Somewhere\Foo\Bar`), then make it root-relative
+ # (`Somewhere\Foo\Bar`), so we end up placing the bytecode file in an
+ # unambiguous `C:\Bytecode\Somewhere\Foo\Bar\`.
+ if not _path_isabs(head):
+ head = _path_join(_os.getcwd(), head)
+
+ # Strip initial drive from a Windows path. We know we have an absolute
+ # path here, so the second part of the check rules out a POSIX path that
+ # happens to contain a colon at the second character.
+ if head[1] == ':' and head[0] not in path_separators:
+ head = head[2:]
+
+ # Strip initial path separator from `head` to complete the conversion
+ # back to a root-relative path before joining.
+ return _path_join(
+ sys.pycache_prefix,
+ head.lstrip(path_separators),
+ filename,
+ )
+ return _path_join(head, _PYCACHE, filename)
- # Strip initial drive from a Windows path. We know we have an absolute
- # path here, so the second part of the check rules out a POSIX path that
- # happens to contain a colon at the second character.
- if head[1] == ':' and head[0] not in path_separators:
- head = head[2:]
- # Strip initial path separator from `head` to complete the conversion
- # back to a root-relative path before joining.
- return _path_join(
- sys.pycache_prefix,
- head.lstrip(path_separators),
- filename,
- )
- return _path_join(head, _PYCACHE, filename)
-
-
def source_from_cache(path):
"""Given the path to a .pyc. file, return the path to its .py file.
@@ -442,29 +442,29 @@ def source_from_cache(path):
raise NotImplementedError('sys.implementation.cache_tag is None')
path = _os.fspath(path)
head, pycache_filename = _path_split(path)
- found_in_pycache_prefix = False
- if sys.pycache_prefix is not None:
- stripped_path = sys.pycache_prefix.rstrip(path_separators)
- if head.startswith(stripped_path + path_sep):
- head = head[len(stripped_path):]
- found_in_pycache_prefix = True
- if not found_in_pycache_prefix:
- head, pycache = _path_split(head)
- if pycache != _PYCACHE:
- raise ValueError(f'{_PYCACHE} not bottom-level directory in '
- f'{path!r}')
+ found_in_pycache_prefix = False
+ if sys.pycache_prefix is not None:
+ stripped_path = sys.pycache_prefix.rstrip(path_separators)
+ if head.startswith(stripped_path + path_sep):
+ head = head[len(stripped_path):]
+ found_in_pycache_prefix = True
+ if not found_in_pycache_prefix:
+ head, pycache = _path_split(head)
+ if pycache != _PYCACHE:
+ raise ValueError(f'{_PYCACHE} not bottom-level directory in '
+ f'{path!r}')
dot_count = pycache_filename.count('.')
if dot_count not in {2, 3}:
- raise ValueError(f'expected only 2 or 3 dots in {pycache_filename!r}')
+ raise ValueError(f'expected only 2 or 3 dots in {pycache_filename!r}')
elif dot_count == 3:
optimization = pycache_filename.rsplit('.', 2)[-2]
if not optimization.startswith(_OPT):
raise ValueError("optimization portion of filename does not start "
- f"with {_OPT!r}")
+ f"with {_OPT!r}")
opt_level = optimization[len(_OPT):]
if not opt_level.isalnum():
- raise ValueError(f"optimization level {optimization!r} is not an "
- "alphanumeric value")
+ raise ValueError(f"optimization level {optimization!r} is not an "
+ "alphanumeric value")
base_filename = pycache_filename.partition('.')[0]
return _path_join(head, base_filename + SOURCE_SUFFIXES[0])
@@ -582,7 +582,7 @@ def _classify_pyc(data, name, exc_details):
message = f'reached EOF while reading pyc header of {name!r}'
_bootstrap._verbose_message('{}', message)
raise EOFError(message)
- flags = _unpack_uint32(data[4:8])
+ flags = _unpack_uint32(data[4:8])
# Only the first two flags are defined.
if flags & ~0b11:
message = f'invalid flags {flags!r} in {name!r}'
@@ -609,12 +609,12 @@ def _validate_timestamp_pyc(data, source_mtime, source_size, name,
An ImportError is raised if the bytecode is stale.
"""
- if _unpack_uint32(data[8:12]) != (source_mtime & 0xFFFFFFFF):
+ if _unpack_uint32(data[8:12]) != (source_mtime & 0xFFFFFFFF):
message = f'bytecode is stale for {name!r}'
_bootstrap._verbose_message('{}', message)
raise ImportError(message, **exc_details)
if (source_size is not None and
- _unpack_uint32(data[12:16]) != (source_size & 0xFFFFFFFF)):
+ _unpack_uint32(data[12:16]) != (source_size & 0xFFFFFFFF)):
raise ImportError(f'bytecode is stale for {name!r}', **exc_details)
@@ -658,9 +658,9 @@ def _compile_bytecode(data, name=None, bytecode_path=None, source_path=None):
def _code_to_timestamp_pyc(code, mtime=0, source_size=0):
"Produce the data for a timestamp-based pyc."
data = bytearray(MAGIC_NUMBER)
- data.extend(_pack_uint32(0))
- data.extend(_pack_uint32(mtime))
- data.extend(_pack_uint32(source_size))
+ data.extend(_pack_uint32(0))
+ data.extend(_pack_uint32(mtime))
+ data.extend(_pack_uint32(source_size))
data.extend(marshal.dumps(code))
return data
@@ -669,7 +669,7 @@ def _code_to_hash_pyc(code, source_hash, checked=True):
"Produce the data for a hash-based pyc."
data = bytearray(MAGIC_NUMBER)
flags = 0b1 | checked << 1
- data.extend(_pack_uint32(flags))
+ data.extend(_pack_uint32(flags))
assert len(source_hash) == 8
data.extend(source_hash)
data.extend(marshal.dumps(code))
@@ -776,9 +776,9 @@ class WindowsRegistryFinder:
@classmethod
def _open_registry(cls, key):
try:
- return winreg.OpenKey(winreg.HKEY_CURRENT_USER, key)
+ return winreg.OpenKey(winreg.HKEY_CURRENT_USER, key)
except OSError:
- return winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key)
+ return winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key)
@classmethod
def _search_registry(cls, fullname):
@@ -790,7 +790,7 @@ class WindowsRegistryFinder:
sys_version='%d.%d' % sys.version_info[:2])
try:
with cls._open_registry(key) as hkey:
- filepath = winreg.QueryValue(hkey, '')
+ filepath = winreg.QueryValue(hkey, '')
except OSError:
return None
return filepath
@@ -858,16 +858,16 @@ class SourceLoader(_LoaderBasics):
def path_mtime(self, path):
"""Optional method that returns the modification time (an int) for the
- specified path (a str).
+ specified path (a str).
Raises OSError when the path cannot be handled.
"""
raise OSError
def path_stats(self, path):
- """Optional method returning a metadata dict for the specified
- path (a str).
-
+ """Optional method returning a metadata dict for the specified
+ path (a str).
+
Possible keys:
- 'mtime' (mandatory) is the numeric timestamp of last source
code modification;
@@ -1035,12 +1035,12 @@ class FileLoader:
def get_data(self, path):
"""Return the data from path as raw bytes."""
- if isinstance(self, (SourceLoader, ExtensionFileLoader)):
- with _io.open_code(str(path)) as file:
- return file.read()
- else:
- with _io.FileIO(path, 'r') as file:
- return file.read()
+ if isinstance(self, (SourceLoader, ExtensionFileLoader)):
+ with _io.open_code(str(path)) as file:
+ return file.read()
+ else:
+ with _io.FileIO(path, 'r') as file:
+ return file.read()
# ResourceReader ABC API.
@@ -1154,11 +1154,11 @@ class ExtensionFileLoader(FileLoader, _LoaderBasics):
def __init__(self, name, path):
self.name = name
- if not _path_isabs(path):
- try:
- path = _path_join(_os.getcwd(), path)
- except OSError:
- pass
+ if not _path_isabs(path):
+ try:
+ path = _path_join(_os.getcwd(), path)
+ except OSError:
+ pass
self.path = path
def __eq__(self, other):
@@ -1245,9 +1245,9 @@ class _NamespacePath:
def __iter__(self):
return iter(self._recalculate())
- def __getitem__(self, index):
- return self._recalculate()[index]
-
+ def __getitem__(self, index):
+ return self._recalculate()[index]
+
def __setitem__(self, index, path):
self._path[index] = path
@@ -1440,20 +1440,20 @@ class PathFinder:
return None
return spec.loader
- @classmethod
- def find_distributions(cls, *args, **kwargs):
- """
- Find distributions.
+ @classmethod
+ def find_distributions(cls, *args, **kwargs):
+ """
+ Find distributions.
+
+ Return an iterable of all Distribution instances capable of
+ loading the metadata for packages matching ``context.name``
+ (or all names if ``None`` indicated) along the paths in the list
+ of directories ``context.path``.
+ """
+ from importlib.metadata import MetadataPathFinder
+ return MetadataPathFinder.find_distributions(*args, **kwargs)
+
- Return an iterable of all Distribution instances capable of
- loading the metadata for packages matching ``context.name``
- (or all names if ``None`` indicated) along the paths in the list
- of directories ``context.path``.
- """
- from importlib.metadata import MetadataPathFinder
- return MetadataPathFinder.find_distributions(*args, **kwargs)
-
-
class FileFinder:
"""File-based finder.
@@ -1473,8 +1473,8 @@ class FileFinder:
self._loaders = loaders
# Base (directory) path
self.path = path or '.'
- if not _path_isabs(self.path):
- self.path = _path_join(_os.getcwd(), self.path)
+ if not _path_isabs(self.path):
+ self.path = _path_join(_os.getcwd(), self.path)
self._path_mtime = -1
self._path_cache = set()
self._relaxed_path_cache = set()
@@ -1537,10 +1537,10 @@ class FileFinder:
is_namespace = _path_isdir(base_path)
# Check for a file w/ a proper suffix exists.
for suffix, loader_class in self._loaders:
- try:
- full_path = _path_join(self.path, tail_module + suffix)
- except ValueError:
- return None
+ try:
+ full_path = _path_join(self.path, tail_module + suffix)
+ except ValueError:
+ return None
_bootstrap._verbose_message('trying {}', full_path, verbosity=2)
if cache_module + suffix in cache:
if _path_isfile(full_path):
@@ -1673,22 +1673,22 @@ def _setup(_bootstrap_module):
continue
else:
raise ImportError('importlib requires posix or nt')
-
+
setattr(self_module, '_os', os_module)
setattr(self_module, 'path_sep', path_sep)
setattr(self_module, 'path_separators', ''.join(path_separators))
- setattr(self_module, '_pathseps_with_colon', {f':{s}' for s in path_separators})
+ setattr(self_module, '_pathseps_with_colon', {f':{s}' for s in path_separators})
- # Directly load built-in modules needed during bootstrap.
- builtin_names = ['_io', '_warnings', 'marshal']
+ # Directly load built-in modules needed during bootstrap.
+ builtin_names = ['_io', '_warnings', 'marshal']
if builtin_os == 'nt':
- builtin_names.append('winreg')
- for builtin_name in builtin_names:
- if builtin_name not in sys.modules:
- builtin_module = _bootstrap._builtin_from_name(builtin_name)
- else:
- builtin_module = sys.modules[builtin_name]
- setattr(self_module, builtin_name, builtin_module)
+ builtin_names.append('winreg')
+ for builtin_name in builtin_names:
+ if builtin_name not in sys.modules:
+ builtin_module = _bootstrap._builtin_from_name(builtin_name)
+ else:
+ builtin_module = sys.modules[builtin_name]
+ setattr(self_module, builtin_name, builtin_module)
# Constants
setattr(self_module, '_relax_case', _make_relax_case())
diff --git a/contrib/tools/python3/src/Lib/importlib/_common.py b/contrib/tools/python3/src/Lib/importlib/_common.py
index 82b02c74585..c1204f0b8f9 100644
--- a/contrib/tools/python3/src/Lib/importlib/_common.py
+++ b/contrib/tools/python3/src/Lib/importlib/_common.py
@@ -1,62 +1,62 @@
-import os
-import pathlib
-import zipfile
-import tempfile
-import functools
-import contextlib
-
-
-def from_package(package):
- """
- Return a Traversable object for the given package.
-
- """
- return fallback_resources(package.__spec__)
-
-
-def fallback_resources(spec):
- package_directory = pathlib.Path(spec.origin).parent
- try:
- archive_path = spec.loader.archive
- rel_path = package_directory.relative_to(archive_path)
- return zipfile.Path(archive_path, str(rel_path) + '/')
- except Exception:
- pass
- return package_directory
-
-
-def _tempfile(reader, suffix=''):
- # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
- # blocks due to the need to close the temporary file to work on Windows
- # properly.
- fd, raw_path = tempfile.mkstemp(suffix=suffix)
- try:
- os.write(fd, reader())
- os.close(fd)
- yield pathlib.Path(raw_path)
- finally:
- try:
- os.remove(raw_path)
- except FileNotFoundError:
- pass
-
-
-def as_file(path):
- """
- Given a Traversable object, return that object as a
- path on the local file system in a context manager.
- """
- with _tempfile(path.read_bytes, suffix=path.name) as local:
- yield local
-
-
-@as_file.register(pathlib.Path)
-def _(path):
- """
- Degenerate behavior for pathlib.Path objects.
- """
- yield path
+import os
+import pathlib
+import zipfile
+import tempfile
+import functools
+import contextlib
+
+
+def from_package(package):
+ """
+ Return a Traversable object for the given package.
+
+ """
+ return fallback_resources(package.__spec__)
+
+
+def fallback_resources(spec):
+ package_directory = pathlib.Path(spec.origin).parent
+ try:
+ archive_path = spec.loader.archive
+ rel_path = package_directory.relative_to(archive_path)
+ return zipfile.Path(archive_path, str(rel_path) + '/')
+ except Exception:
+ pass
+ return package_directory
+
+
+def _tempfile(reader, suffix=''):
+ # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
+ # blocks due to the need to close the temporary file to work on Windows
+ # properly.
+ fd, raw_path = tempfile.mkstemp(suffix=suffix)
+ try:
+ os.write(fd, reader())
+ os.close(fd)
+ yield pathlib.Path(raw_path)
+ finally:
+ try:
+ os.remove(raw_path)
+ except FileNotFoundError:
+ pass
+
+
+def as_file(path):
+ """
+ Given a Traversable object, return that object as a
+ path on the local file system in a context manager.
+ """
+ with _tempfile(path.read_bytes, suffix=path.name) as local:
+ yield local
+
+
+@as_file.register(pathlib.Path)
+def _(path):
+ """
+ Degenerate behavior for pathlib.Path objects.
+ """
+ yield path
diff --git a/contrib/tools/python3/src/Lib/importlib/abc.py b/contrib/tools/python3/src/Lib/importlib/abc.py
index def2e342dc3..b8a9bb1a21e 100644
--- a/contrib/tools/python3/src/Lib/importlib/abc.py
+++ b/contrib/tools/python3/src/Lib/importlib/abc.py
@@ -10,11 +10,11 @@ except ImportError as exc:
_frozen_importlib = None
try:
import _frozen_importlib_external
-except ImportError:
+except ImportError:
_frozen_importlib_external = _bootstrap_external
import abc
import warnings
-from typing import Protocol, runtime_checkable
+from typing import Protocol, runtime_checkable
def _register(abstract_cls, *classes):
@@ -387,88 +387,88 @@ class ResourceReader(metaclass=abc.ABCMeta):
_register(ResourceReader, machinery.SourceFileLoader)
-
-
-@runtime_checkable
-class Traversable(Protocol):
- """
- An object with a subset of pathlib.Path methods suitable for
- traversing directories and opening files.
- """
-
- @abc.abstractmethod
- def iterdir(self):
- """
- Yield Traversable objects in self
- """
-
- @abc.abstractmethod
- def read_bytes(self):
- """
- Read contents of self as bytes
- """
-
- @abc.abstractmethod
- def read_text(self, encoding=None):
- """
- Read contents of self as bytes
- """
-
- @abc.abstractmethod
- def is_dir(self):
- """
- Return True if self is a dir
- """
-
- @abc.abstractmethod
- def is_file(self):
- """
- Return True if self is a file
- """
-
- @abc.abstractmethod
- def joinpath(self, child):
- """
- Return Traversable child in self
- """
-
- @abc.abstractmethod
- def __truediv__(self, child):
- """
- Return Traversable child in self
- """
-
- @abc.abstractmethod
- def open(self, mode='r', *args, **kwargs):
- """
- mode may be 'r' or 'rb' to open as text or binary. Return a handle
- suitable for reading (same as pathlib.Path.open).
-
- When opening as text, accepts encoding parameters such as those
- accepted by io.TextIOWrapper.
- """
-
- @abc.abstractproperty
- def name(self):
- # type: () -> str
- """
- The base name of this object without any parent references.
- """
-
-
-class TraversableResources(ResourceReader):
- @abc.abstractmethod
- def files(self):
- """Return a Traversable object for the loaded package."""
-
- def open_resource(self, resource):
- return self.files().joinpath(resource).open('rb')
-
- def resource_path(self, resource):
- raise FileNotFoundError(resource)
-
- def is_resource(self, path):
- return self.files().joinpath(path).isfile()
-
- def contents(self):
- return (item.name for item in self.files().iterdir())
+
+
+@runtime_checkable
+class Traversable(Protocol):
+ """
+ An object with a subset of pathlib.Path methods suitable for
+ traversing directories and opening files.
+ """
+
+ @abc.abstractmethod
+ def iterdir(self):
+ """
+ Yield Traversable objects in self
+ """
+
+ @abc.abstractmethod
+ def read_bytes(self):
+ """
+ Read contents of self as bytes
+ """
+
+ @abc.abstractmethod
+ def read_text(self, encoding=None):
+ """
+ Read contents of self as bytes
+ """
+
+ @abc.abstractmethod
+ def is_dir(self):
+ """
+ Return True if self is a dir
+ """
+
+ @abc.abstractmethod
+ def is_file(self):
+ """
+ Return True if self is a file
+ """
+
+ @abc.abstractmethod
+ def joinpath(self, child):
+ """
+ Return Traversable child in self
+ """
+
+ @abc.abstractmethod
+ def __truediv__(self, child):
+ """
+ Return Traversable child in self
+ """
+
+ @abc.abstractmethod
+ def open(self, mode='r', *args, **kwargs):
+ """
+ mode may be 'r' or 'rb' to open as text or binary. Return a handle
+ suitable for reading (same as pathlib.Path.open).
+
+ When opening as text, accepts encoding parameters such as those
+ accepted by io.TextIOWrapper.
+ """
+
+ @abc.abstractproperty
+ def name(self):
+ # type: () -> str
+ """
+ The base name of this object without any parent references.
+ """
+
+
+class TraversableResources(ResourceReader):
+ @abc.abstractmethod
+ def files(self):
+ """Return a Traversable object for the loaded package."""
+
+ def open_resource(self, resource):
+ return self.files().joinpath(resource).open('rb')
+
+ def resource_path(self, resource):
+ raise FileNotFoundError(resource)
+
+ def is_resource(self, path):
+ return self.files().joinpath(path).isfile()
+
+ def contents(self):
+ return (item.name for item in self.files().iterdir())
diff --git a/contrib/tools/python3/src/Lib/importlib/metadata.py b/contrib/tools/python3/src/Lib/importlib/metadata.py
index c48aa9e7658..594986ce23e 100644
--- a/contrib/tools/python3/src/Lib/importlib/metadata.py
+++ b/contrib/tools/python3/src/Lib/importlib/metadata.py
@@ -1,595 +1,595 @@
-import io
-import os
-import re
-import abc
-import csv
-import sys
-import email
-import pathlib
-import zipfile
-import operator
-import functools
-import itertools
-import posixpath
-import collections
-
-from configparser import ConfigParser
-from contextlib import suppress
-from importlib import import_module
-from importlib.abc import MetaPathFinder
-from itertools import starmap
-
-
-__all__ = [
- 'Distribution',
- 'DistributionFinder',
- 'PackageNotFoundError',
- 'distribution',
- 'distributions',
- 'entry_points',
- 'files',
- 'metadata',
- 'requires',
- 'version',
- ]
-
-
-class PackageNotFoundError(ModuleNotFoundError):
- """The package was not found."""
-
-
-class EntryPoint(
- collections.namedtuple('EntryPointBase', 'name value group')):
- """An entry point as defined by Python packaging conventions.
-
- See `the packaging docs on entry points
- <https://packaging.python.org/specifications/entry-points/>`_
- for more information.
- """
-
- pattern = re.compile(
- r'(?P<module>[\w.]+)\s*'
- r'(:\s*(?P<attr>[\w.]+))?\s*'
- r'(?P<extras>\[.*\])?\s*$'
- )
- """
- A regular expression describing the syntax for an entry point,
- which might look like:
-
- - module
- - package.module
- - package.module:attribute
- - package.module:object.attribute
- - package.module:attr [extra1, extra2]
-
- Other combinations are possible as well.
-
- The expression is lenient about whitespace around the ':',
- following the attr, and following any extras.
- """
-
- def load(self):
- """Load the entry point from its definition. If only a module
- is indicated by the value, return that module. Otherwise,
- return the named object.
- """
- match = self.pattern.match(self.value)
- module = import_module(match.group('module'))
- attrs = filter(None, (match.group('attr') or '').split('.'))
- return functools.reduce(getattr, attrs, module)
-
- @property
- def module(self):
- match = self.pattern.match(self.value)
- return match.group('module')
-
- @property
- def attr(self):
- match = self.pattern.match(self.value)
- return match.group('attr')
-
- @property
- def extras(self):
- match = self.pattern.match(self.value)
- return list(re.finditer(r'\w+', match.group('extras') or ''))
-
- @classmethod
- def _from_config(cls, config):
- return [
- cls(name, value, group)
- for group in config.sections()
- for name, value in config.items(group)
- ]
-
- @classmethod
- def _from_text(cls, text):
- config = ConfigParser(delimiters='=')
- # case sensitive: https://stackoverflow.com/q/1611799/812183
- config.optionxform = str
- try:
- config.read_string(text)
- except AttributeError: # pragma: nocover
- # Python 2 has no read_string
- config.readfp(io.StringIO(text))
- return EntryPoint._from_config(config)
-
- def __iter__(self):
- """
- Supply iter so one may construct dicts of EntryPoints easily.
- """
- return iter((self.name, self))
-
- def __reduce__(self):
- return (
- self.__class__,
- (self.name, self.value, self.group),
- )
-
-
-class PackagePath(pathlib.PurePosixPath):
- """A reference to a path in a package"""
-
- def read_text(self, encoding='utf-8'):
- with self.locate().open(encoding=encoding) as stream:
- return stream.read()
-
- def read_binary(self):
- with self.locate().open('rb') as stream:
- return stream.read()
-
- def locate(self):
- """Return a path-like object for this path"""
- return self.dist.locate_file(self)
-
-
-class FileHash:
- def __init__(self, spec):
- self.mode, _, self.value = spec.partition('=')
-
- def __repr__(self):
- return '<FileHash mode: {} value: {}>'.format(self.mode, self.value)
-
-
-class Distribution:
- """A Python distribution package."""
-
- @abc.abstractmethod
- def read_text(self, filename):
- """Attempt to load metadata file given by the name.
-
- :param filename: The name of the file in the distribution info.
- :return: The text if found, otherwise None.
- """
-
- @abc.abstractmethod
- def locate_file(self, path):
- """
- Given a path to a file in this distribution, return a path
- to it.
- """
-
- @classmethod
- def from_name(cls, name):
- """Return the Distribution for the given package name.
-
- :param name: The name of the distribution package to search for.
- :return: The Distribution instance (or subclass thereof) for the named
- package, if found.
- :raises PackageNotFoundError: When the named package's distribution
- metadata cannot be found.
- """
- for resolver in cls._discover_resolvers():
- dists = resolver(DistributionFinder.Context(name=name))
- dist = next(iter(dists), None)
- if dist is not None:
- return dist
- else:
- raise PackageNotFoundError(name)
-
- @classmethod
- def discover(cls, **kwargs):
- """Return an iterable of Distribution objects for all packages.
-
- Pass a ``context`` or pass keyword arguments for constructing
- a context.
-
- :context: A ``DistributionFinder.Context`` object.
- :return: Iterable of Distribution objects for all packages.
- """
- context = kwargs.pop('context', None)
- if context and kwargs:
- raise ValueError("cannot accept context and kwargs")
- context = context or DistributionFinder.Context(**kwargs)
- return itertools.chain.from_iterable(
- resolver(context)
- for resolver in cls._discover_resolvers()
- )
-
- @staticmethod
- def at(path):
- """Return a Distribution for the indicated metadata path
-
- :param path: a string or path-like object
- :return: a concrete Distribution instance for the path
- """
- return PathDistribution(pathlib.Path(path))
-
- @staticmethod
- def _discover_resolvers():
- """Search the meta_path for resolvers."""
- declared = (
- getattr(finder, 'find_distributions', None)
- for finder in sys.meta_path
- )
- return filter(None, declared)
-
- @classmethod
- def _local(cls, root='.'):
- from pep517 import build, meta
- system = build.compat_system(root)
- builder = functools.partial(
- meta.build,
- source_dir=root,
- system=system,
- )
- return PathDistribution(zipfile.Path(meta.build_as_zip(builder)))
-
- @property
- def metadata(self):
- """Return the parsed metadata for this Distribution.
-
- The returned object will have keys that name the various bits of
- metadata. See PEP 566 for details.
- """
- text = (
- self.read_text('METADATA')
- or self.read_text('PKG-INFO')
- # This last clause is here to support old egg-info files. Its
- # effect is to just end up using the PathDistribution's self._path
- # (which points to the egg-info file) attribute unchanged.
- or self.read_text('')
- )
- return email.message_from_string(text)
-
- @property
- def version(self):
- """Return the 'Version' metadata for the distribution package."""
- return self.metadata['Version']
-
- @property
- def entry_points(self):
- return EntryPoint._from_text(self.read_text('entry_points.txt'))
-
- @property
- def files(self):
- """Files in this distribution.
-
- :return: List of PackagePath for this distribution or None
-
- Result is `None` if the metadata file that enumerates files
- (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
- missing.
- Result may be empty if the metadata exists but is empty.
- """
- file_lines = self._read_files_distinfo() or self._read_files_egginfo()
-
- def make_file(name, hash=None, size_str=None):
- result = PackagePath(name)
- result.hash = FileHash(hash) if hash else None
- result.size = int(size_str) if size_str else None
- result.dist = self
- return result
-
- return file_lines and list(starmap(make_file, csv.reader(file_lines)))
-
- def _read_files_distinfo(self):
- """
- Read the lines of RECORD
- """
- text = self.read_text('RECORD')
- return text and text.splitlines()
-
- def _read_files_egginfo(self):
- """
- SOURCES.txt might contain literal commas, so wrap each line
- in quotes.
- """
- text = self.read_text('SOURCES.txt')
- return text and map('"{}"'.format, text.splitlines())
-
- @property
- def requires(self):
- """Generated requirements specified for this Distribution"""
- reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
- return reqs and list(reqs)
-
- def _read_dist_info_reqs(self):
- return self.metadata.get_all('Requires-Dist')
-
- def _read_egg_info_reqs(self):
- source = self.read_text('requires.txt')
- return source and self._deps_from_requires_text(source)
-
- @classmethod
- def _deps_from_requires_text(cls, source):
- section_pairs = cls._read_sections(source.splitlines())
- sections = {
- section: list(map(operator.itemgetter('line'), results))
- for section, results in
- itertools.groupby(section_pairs, operator.itemgetter('section'))
- }
- return cls._convert_egg_info_reqs_to_simple_reqs(sections)
-
- @staticmethod
- def _read_sections(lines):
- section = None
- for line in filter(None, lines):
- section_match = re.match(r'\[(.*)\]$', line)
- if section_match:
- section = section_match.group(1)
- continue
- yield locals()
-
- @staticmethod
- def _convert_egg_info_reqs_to_simple_reqs(sections):
- """
- Historically, setuptools would solicit and store 'extra'
- requirements, including those with environment markers,
- in separate sections. More modern tools expect each
- dependency to be defined separately, with any relevant
- extras and environment markers attached directly to that
- requirement. This method converts the former to the
- latter. See _test_deps_from_requires_text for an example.
- """
- def make_condition(name):
- return name and 'extra == "{name}"'.format(name=name)
-
- 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, deps in sections.items():
- for dep in deps:
- space = url_req_space(dep)
- yield dep + space + quoted_marker(section)
-
-
-class DistributionFinder(MetaPathFinder):
- """
- A MetaPathFinder capable of discovering installed distributions.
- """
-
- class Context:
- """
- Keyword arguments presented by the caller to
- ``distributions()`` or ``Distribution.discover()``
- to narrow the scope of a search for distributions
- in all DistributionFinders.
-
- Each DistributionFinder may expect any parameters
- and should attempt to honor the canonical
- parameters defined below when appropriate.
- """
-
- name = None
- """
- Specific name for which a distribution finder should match.
- A name of ``None`` matches all distributions.
- """
-
- def __init__(self, **kwargs):
- vars(self).update(kwargs)
-
- @property
- def path(self):
- """
- The path that a distribution finder should search.
-
- Typically refers to Python package paths and defaults
- to ``sys.path``.
- """
- return vars(self).get('path', sys.path)
-
- @abc.abstractmethod
- def find_distributions(self, context=Context()):
- """
- Find distributions.
-
- Return an iterable of all Distribution instances capable of
- loading the metadata for packages matching the ``context``,
- a DistributionFinder.Context instance.
- """
-
-
-class FastPath:
- """
- Micro-optimized class for searching a path for
- children.
- """
-
- 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 '.')
- with suppress(Exception):
- return self.zip_children()
- return []
-
- def zip_children(self):
- zip_path = zipfile.Path(self.root)
- names = zip_path.root.namelist()
- self.joinpath = zip_path.joinpath
-
- return dict.fromkeys(
- child.split(posixpath.sep, 1)[0]
- for child in names
- )
-
- def is_egg(self, search):
- base = self.base
- return (
- base == search.versionless_egg_name
- or base.startswith(search.prefix)
- and base.endswith('.egg'))
-
- def search(self, name):
- for child in self.children():
- n_low = child.lower()
- if (n_low in name.exact_matches
- or n_low.startswith(name.prefix)
- and n_low.endswith(name.suffixes)
- # legacy case:
- or self.is_egg(name) and n_low == 'egg-info'):
- yield self.joinpath(child)
-
-
-class Prepared:
- """
- A prepared search for metadata on a possibly-named package.
- """
- normalized = ''
- prefix = ''
- suffixes = '.dist-info', '.egg-info'
- exact_matches = [''][:0]
- versionless_egg_name = ''
-
- def __init__(self, name):
- self.name = name
- if name is None:
- return
- self.normalized = name.lower().replace('-', '_')
- self.prefix = self.normalized + '-'
- self.exact_matches = [
- self.normalized + suffix for suffix in self.suffixes]
- self.versionless_egg_name = self.normalized + '.egg'
-
-
-class MetadataPathFinder(DistributionFinder):
- @classmethod
- def find_distributions(cls, context=DistributionFinder.Context()):
- """
- Find distributions.
-
- Return an iterable of all Distribution instances capable of
- loading the metadata for packages matching ``context.name``
- (or all names if ``None`` indicated) along the paths in the list
- of directories ``context.path``.
- """
- found = cls._search_paths(context.name, context.path)
- return map(PathDistribution, found)
-
- @classmethod
- def _search_paths(cls, name, paths):
- """Find metadata directories in paths heuristically."""
- return itertools.chain.from_iterable(
- path.search(Prepared(name))
- for path in map(FastPath, paths)
- )
-
-
-class PathDistribution(Distribution):
- def __init__(self, path):
- """Construct a distribution from a path to the metadata directory.
-
- :param path: A pathlib.Path or similar object supporting
- .joinpath(), __div__, .parent, and .read_text().
- """
- self._path = path
-
- def read_text(self, filename):
- with suppress(FileNotFoundError, IsADirectoryError, KeyError,
- NotADirectoryError, PermissionError):
- return self._path.joinpath(filename).read_text(encoding='utf-8')
- read_text.__doc__ = Distribution.read_text.__doc__
-
- def locate_file(self, path):
- return self._path.parent / path
-
-
-def distribution(distribution_name):
- """Get the ``Distribution`` instance for the named package.
-
- :param distribution_name: The name of the distribution package as a string.
- :return: A ``Distribution`` instance (or subclass thereof).
- """
- return Distribution.from_name(distribution_name)
-
-
-def distributions(**kwargs):
- """Get all ``Distribution`` instances in the current environment.
-
- :return: An iterable of ``Distribution`` instances.
- """
- return Distribution.discover(**kwargs)
-
-
-def metadata(distribution_name):
- """Get the metadata for the named package.
-
- :param distribution_name: The name of the distribution package to query.
- :return: An email.Message containing the parsed metadata.
- """
- return Distribution.from_name(distribution_name).metadata
-
-
-def version(distribution_name):
- """Get the version string for the named package.
-
- :param distribution_name: The name of the distribution package to query.
- :return: The version string for the package as defined in the package's
- "Version" metadata key.
- """
- return distribution(distribution_name).version
-
-
-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())
- by_group = operator.attrgetter('group')
- ordered = sorted(eps, key=by_group)
- grouped = itertools.groupby(ordered, by_group)
- return {
- group: tuple(eps)
- for group, eps in grouped
- }
-
-
-def files(distribution_name):
- """Return a list of files for the named package.
-
- :param distribution_name: The name of the distribution package to query.
- :return: List of files composing the distribution.
- """
- return distribution(distribution_name).files
-
-
-def requires(distribution_name):
- """
- Return a list of requirements for the named package.
-
- :return: An iterator of requirements, suitable for
- packaging.requirement.Requirement.
- """
- return distribution(distribution_name).requires
+import io
+import os
+import re
+import abc
+import csv
+import sys
+import email
+import pathlib
+import zipfile
+import operator
+import functools
+import itertools
+import posixpath
+import collections
+
+from configparser import ConfigParser
+from contextlib import suppress
+from importlib import import_module
+from importlib.abc import MetaPathFinder
+from itertools import starmap
+
+
+__all__ = [
+ 'Distribution',
+ 'DistributionFinder',
+ 'PackageNotFoundError',
+ 'distribution',
+ 'distributions',
+ 'entry_points',
+ 'files',
+ 'metadata',
+ 'requires',
+ 'version',
+ ]
+
+
+class PackageNotFoundError(ModuleNotFoundError):
+ """The package was not found."""
+
+
+class EntryPoint(
+ collections.namedtuple('EntryPointBase', 'name value group')):
+ """An entry point as defined by Python packaging conventions.
+
+ See `the packaging docs on entry points
+ <https://packaging.python.org/specifications/entry-points/>`_
+ for more information.
+ """
+
+ pattern = re.compile(
+ r'(?P<module>[\w.]+)\s*'
+ r'(:\s*(?P<attr>[\w.]+))?\s*'
+ r'(?P<extras>\[.*\])?\s*$'
+ )
+ """
+ A regular expression describing the syntax for an entry point,
+ which might look like:
+
+ - module
+ - package.module
+ - package.module:attribute
+ - package.module:object.attribute
+ - package.module:attr [extra1, extra2]
+
+ Other combinations are possible as well.
+
+ The expression is lenient about whitespace around the ':',
+ following the attr, and following any extras.
+ """
+
+ def load(self):
+ """Load the entry point from its definition. If only a module
+ is indicated by the value, return that module. Otherwise,
+ return the named object.
+ """
+ match = self.pattern.match(self.value)
+ module = import_module(match.group('module'))
+ attrs = filter(None, (match.group('attr') or '').split('.'))
+ return functools.reduce(getattr, attrs, module)
+
+ @property
+ def module(self):
+ match = self.pattern.match(self.value)
+ return match.group('module')
+
+ @property
+ def attr(self):
+ match = self.pattern.match(self.value)
+ return match.group('attr')
+
+ @property
+ def extras(self):
+ match = self.pattern.match(self.value)
+ return list(re.finditer(r'\w+', match.group('extras') or ''))
+
+ @classmethod
+ def _from_config(cls, config):
+ return [
+ cls(name, value, group)
+ for group in config.sections()
+ for name, value in config.items(group)
+ ]
+
+ @classmethod
+ def _from_text(cls, text):
+ config = ConfigParser(delimiters='=')
+ # case sensitive: https://stackoverflow.com/q/1611799/812183
+ config.optionxform = str
+ try:
+ config.read_string(text)
+ except AttributeError: # pragma: nocover
+ # Python 2 has no read_string
+ config.readfp(io.StringIO(text))
+ return EntryPoint._from_config(config)
+
+ def __iter__(self):
+ """
+ Supply iter so one may construct dicts of EntryPoints easily.
+ """
+ return iter((self.name, self))
+
+ def __reduce__(self):
+ return (
+ self.__class__,
+ (self.name, self.value, self.group),
+ )
+
+
+class PackagePath(pathlib.PurePosixPath):
+ """A reference to a path in a package"""
+
+ def read_text(self, encoding='utf-8'):
+ with self.locate().open(encoding=encoding) as stream:
+ return stream.read()
+
+ def read_binary(self):
+ with self.locate().open('rb') as stream:
+ return stream.read()
+
+ def locate(self):
+ """Return a path-like object for this path"""
+ return self.dist.locate_file(self)
+
+
+class FileHash:
+ def __init__(self, spec):
+ self.mode, _, self.value = spec.partition('=')
+
+ def __repr__(self):
+ return '<FileHash mode: {} value: {}>'.format(self.mode, self.value)
+
+
+class Distribution:
+ """A Python distribution package."""
+
+ @abc.abstractmethod
+ def read_text(self, filename):
+ """Attempt to load metadata file given by the name.
+
+ :param filename: The name of the file in the distribution info.
+ :return: The text if found, otherwise None.
+ """
+
+ @abc.abstractmethod
+ def locate_file(self, path):
+ """
+ Given a path to a file in this distribution, return a path
+ to it.
+ """
+
+ @classmethod
+ def from_name(cls, name):
+ """Return the Distribution for the given package name.
+
+ :param name: The name of the distribution package to search for.
+ :return: The Distribution instance (or subclass thereof) for the named
+ package, if found.
+ :raises PackageNotFoundError: When the named package's distribution
+ metadata cannot be found.
+ """
+ for resolver in cls._discover_resolvers():
+ dists = resolver(DistributionFinder.Context(name=name))
+ dist = next(iter(dists), None)
+ if dist is not None:
+ return dist
+ else:
+ raise PackageNotFoundError(name)
+
+ @classmethod
+ def discover(cls, **kwargs):
+ """Return an iterable of Distribution objects for all packages.
+
+ Pass a ``context`` or pass keyword arguments for constructing
+ a context.
+
+ :context: A ``DistributionFinder.Context`` object.
+ :return: Iterable of Distribution objects for all packages.
+ """
+ context = kwargs.pop('context', None)
+ if context and kwargs:
+ raise ValueError("cannot accept context and kwargs")
+ context = context or DistributionFinder.Context(**kwargs)
+ return itertools.chain.from_iterable(
+ resolver(context)
+ for resolver in cls._discover_resolvers()
+ )
+
+ @staticmethod
+ def at(path):
+ """Return a Distribution for the indicated metadata path
+
+ :param path: a string or path-like object
+ :return: a concrete Distribution instance for the path
+ """
+ return PathDistribution(pathlib.Path(path))
+
+ @staticmethod
+ def _discover_resolvers():
+ """Search the meta_path for resolvers."""
+ declared = (
+ getattr(finder, 'find_distributions', None)
+ for finder in sys.meta_path
+ )
+ return filter(None, declared)
+
+ @classmethod
+ def _local(cls, root='.'):
+ from pep517 import build, meta
+ system = build.compat_system(root)
+ builder = functools.partial(
+ meta.build,
+ source_dir=root,
+ system=system,
+ )
+ return PathDistribution(zipfile.Path(meta.build_as_zip(builder)))
+
+ @property
+ def metadata(self):
+ """Return the parsed metadata for this Distribution.
+
+ The returned object will have keys that name the various bits of
+ metadata. See PEP 566 for details.
+ """
+ text = (
+ self.read_text('METADATA')
+ or self.read_text('PKG-INFO')
+ # This last clause is here to support old egg-info files. Its
+ # effect is to just end up using the PathDistribution's self._path
+ # (which points to the egg-info file) attribute unchanged.
+ or self.read_text('')
+ )
+ return email.message_from_string(text)
+
+ @property
+ def version(self):
+ """Return the 'Version' metadata for the distribution package."""
+ return self.metadata['Version']
+
+ @property
+ def entry_points(self):
+ return EntryPoint._from_text(self.read_text('entry_points.txt'))
+
+ @property
+ def files(self):
+ """Files in this distribution.
+
+ :return: List of PackagePath for this distribution or None
+
+ Result is `None` if the metadata file that enumerates files
+ (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
+ missing.
+ Result may be empty if the metadata exists but is empty.
+ """
+ file_lines = self._read_files_distinfo() or self._read_files_egginfo()
+
+ def make_file(name, hash=None, size_str=None):
+ result = PackagePath(name)
+ result.hash = FileHash(hash) if hash else None
+ result.size = int(size_str) if size_str else None
+ result.dist = self
+ return result
+
+ return file_lines and list(starmap(make_file, csv.reader(file_lines)))
+
+ def _read_files_distinfo(self):
+ """
+ Read the lines of RECORD
+ """
+ text = self.read_text('RECORD')
+ return text and text.splitlines()
+
+ def _read_files_egginfo(self):
+ """
+ SOURCES.txt might contain literal commas, so wrap each line
+ in quotes.
+ """
+ text = self.read_text('SOURCES.txt')
+ return text and map('"{}"'.format, text.splitlines())
+
+ @property
+ def requires(self):
+ """Generated requirements specified for this Distribution"""
+ reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
+ return reqs and list(reqs)
+
+ def _read_dist_info_reqs(self):
+ return self.metadata.get_all('Requires-Dist')
+
+ def _read_egg_info_reqs(self):
+ source = self.read_text('requires.txt')
+ return source and self._deps_from_requires_text(source)
+
+ @classmethod
+ def _deps_from_requires_text(cls, source):
+ section_pairs = cls._read_sections(source.splitlines())
+ sections = {
+ section: list(map(operator.itemgetter('line'), results))
+ for section, results in
+ itertools.groupby(section_pairs, operator.itemgetter('section'))
+ }
+ return cls._convert_egg_info_reqs_to_simple_reqs(sections)
+
+ @staticmethod
+ def _read_sections(lines):
+ section = None
+ for line in filter(None, lines):
+ section_match = re.match(r'\[(.*)\]$', line)
+ if section_match:
+ section = section_match.group(1)
+ continue
+ yield locals()
+
+ @staticmethod
+ def _convert_egg_info_reqs_to_simple_reqs(sections):
+ """
+ Historically, setuptools would solicit and store 'extra'
+ requirements, including those with environment markers,
+ in separate sections. More modern tools expect each
+ dependency to be defined separately, with any relevant
+ extras and environment markers attached directly to that
+ requirement. This method converts the former to the
+ latter. See _test_deps_from_requires_text for an example.
+ """
+ def make_condition(name):
+ return name and 'extra == "{name}"'.format(name=name)
+
+ 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, deps in sections.items():
+ for dep in deps:
+ space = url_req_space(dep)
+ yield dep + space + quoted_marker(section)
+
+
+class DistributionFinder(MetaPathFinder):
+ """
+ A MetaPathFinder capable of discovering installed distributions.
+ """
+
+ class Context:
+ """
+ Keyword arguments presented by the caller to
+ ``distributions()`` or ``Distribution.discover()``
+ to narrow the scope of a search for distributions
+ in all DistributionFinders.
+
+ Each DistributionFinder may expect any parameters
+ and should attempt to honor the canonical
+ parameters defined below when appropriate.
+ """
+
+ name = None
+ """
+ Specific name for which a distribution finder should match.
+ A name of ``None`` matches all distributions.
+ """
+
+ def __init__(self, **kwargs):
+ vars(self).update(kwargs)
+
+ @property
+ def path(self):
+ """
+ The path that a distribution finder should search.
+
+ Typically refers to Python package paths and defaults
+ to ``sys.path``.
+ """
+ return vars(self).get('path', sys.path)
+
+ @abc.abstractmethod
+ def find_distributions(self, context=Context()):
+ """
+ Find distributions.
+
+ Return an iterable of all Distribution instances capable of
+ loading the metadata for packages matching the ``context``,
+ a DistributionFinder.Context instance.
+ """
+
+
+class FastPath:
+ """
+ Micro-optimized class for searching a path for
+ children.
+ """
+
+ 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 '.')
+ with suppress(Exception):
+ return self.zip_children()
+ return []
+
+ def zip_children(self):
+ zip_path = zipfile.Path(self.root)
+ names = zip_path.root.namelist()
+ self.joinpath = zip_path.joinpath
+
+ return dict.fromkeys(
+ child.split(posixpath.sep, 1)[0]
+ for child in names
+ )
+
+ def is_egg(self, search):
+ base = self.base
+ return (
+ base == search.versionless_egg_name
+ or base.startswith(search.prefix)
+ and base.endswith('.egg'))
+
+ def search(self, name):
+ for child in self.children():
+ n_low = child.lower()
+ if (n_low in name.exact_matches
+ or n_low.startswith(name.prefix)
+ and n_low.endswith(name.suffixes)
+ # legacy case:
+ or self.is_egg(name) and n_low == 'egg-info'):
+ yield self.joinpath(child)
+
+
+class Prepared:
+ """
+ A prepared search for metadata on a possibly-named package.
+ """
+ normalized = ''
+ prefix = ''
+ suffixes = '.dist-info', '.egg-info'
+ exact_matches = [''][:0]
+ versionless_egg_name = ''
+
+ def __init__(self, name):
+ self.name = name
+ if name is None:
+ return
+ self.normalized = name.lower().replace('-', '_')
+ self.prefix = self.normalized + '-'
+ self.exact_matches = [
+ self.normalized + suffix for suffix in self.suffixes]
+ self.versionless_egg_name = self.normalized + '.egg'
+
+
+class MetadataPathFinder(DistributionFinder):
+ @classmethod
+ def find_distributions(cls, context=DistributionFinder.Context()):
+ """
+ Find distributions.
+
+ Return an iterable of all Distribution instances capable of
+ loading the metadata for packages matching ``context.name``
+ (or all names if ``None`` indicated) along the paths in the list
+ of directories ``context.path``.
+ """
+ found = cls._search_paths(context.name, context.path)
+ return map(PathDistribution, found)
+
+ @classmethod
+ def _search_paths(cls, name, paths):
+ """Find metadata directories in paths heuristically."""
+ return itertools.chain.from_iterable(
+ path.search(Prepared(name))
+ for path in map(FastPath, paths)
+ )
+
+
+class PathDistribution(Distribution):
+ def __init__(self, path):
+ """Construct a distribution from a path to the metadata directory.
+
+ :param path: A pathlib.Path or similar object supporting
+ .joinpath(), __div__, .parent, and .read_text().
+ """
+ self._path = path
+
+ def read_text(self, filename):
+ with suppress(FileNotFoundError, IsADirectoryError, KeyError,
+ NotADirectoryError, PermissionError):
+ return self._path.joinpath(filename).read_text(encoding='utf-8')
+ read_text.__doc__ = Distribution.read_text.__doc__
+
+ def locate_file(self, path):
+ return self._path.parent / path
+
+
+def distribution(distribution_name):
+ """Get the ``Distribution`` instance for the named package.
+
+ :param distribution_name: The name of the distribution package as a string.
+ :return: A ``Distribution`` instance (or subclass thereof).
+ """
+ return Distribution.from_name(distribution_name)
+
+
+def distributions(**kwargs):
+ """Get all ``Distribution`` instances in the current environment.
+
+ :return: An iterable of ``Distribution`` instances.
+ """
+ return Distribution.discover(**kwargs)
+
+
+def metadata(distribution_name):
+ """Get the metadata for the named package.
+
+ :param distribution_name: The name of the distribution package to query.
+ :return: An email.Message containing the parsed metadata.
+ """
+ return Distribution.from_name(distribution_name).metadata
+
+
+def version(distribution_name):
+ """Get the version string for the named package.
+
+ :param distribution_name: The name of the distribution package to query.
+ :return: The version string for the package as defined in the package's
+ "Version" metadata key.
+ """
+ return distribution(distribution_name).version
+
+
+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())
+ by_group = operator.attrgetter('group')
+ ordered = sorted(eps, key=by_group)
+ grouped = itertools.groupby(ordered, by_group)
+ return {
+ group: tuple(eps)
+ for group, eps in grouped
+ }
+
+
+def files(distribution_name):
+ """Return a list of files for the named package.
+
+ :param distribution_name: The name of the distribution package to query.
+ :return: List of files composing the distribution.
+ """
+ return distribution(distribution_name).files
+
+
+def requires(distribution_name):
+ """
+ Return a list of requirements for the named package.
+
+ :return: An iterator of requirements, suitable for
+ packaging.requirement.Requirement.
+ """
+ return distribution(distribution_name).requires
diff --git a/contrib/tools/python3/src/Lib/importlib/resources.py b/contrib/tools/python3/src/Lib/importlib/resources.py
index 7d03311cf46..b803a01c91d 100644
--- a/contrib/tools/python3/src/Lib/importlib/resources.py
+++ b/contrib/tools/python3/src/Lib/importlib/resources.py
@@ -1,15 +1,15 @@
import os
from . import abc as resources_abc
-from . import _common
-from ._common import as_file
+from . import _common
+from ._common import as_file
from contextlib import contextmanager, suppress
from importlib import import_module
from importlib.abc import ResourceLoader
from io import BytesIO, TextIOWrapper
from pathlib import Path
from types import ModuleType
-from typing import ContextManager, Iterable, Optional, Union
+from typing import ContextManager, Iterable, Optional, Union
from typing import cast
from typing.io import BinaryIO, TextIO
@@ -17,9 +17,9 @@ from typing.io import BinaryIO, TextIO
__all__ = [
'Package',
'Resource',
- 'as_file',
+ 'as_file',
'contents',
- 'files',
+ 'files',
'is_resource',
'open_binary',
'open_text',
@@ -33,23 +33,23 @@ Package = Union[str, ModuleType]
Resource = Union[str, os.PathLike]
-def _resolve(name) -> ModuleType:
- """If name is a string, resolve to a module."""
- if hasattr(name, '__spec__'):
- return name
- return import_module(name)
-
-
+def _resolve(name) -> ModuleType:
+ """If name is a string, resolve to a module."""
+ if hasattr(name, '__spec__'):
+ return name
+ return import_module(name)
+
+
def _get_package(package) -> ModuleType:
"""Take a package name or module object and return the module.
- If a name, the module is imported. If the resolved module
+ If a name, the module is imported. If the resolved module
object is not a package, raise an exception.
"""
- module = _resolve(package)
- if module.__spec__.submodule_search_locations is None:
- raise TypeError('{!r} is not a package'.format(package))
- return module
+ module = _resolve(package)
+ if module.__spec__.submodule_search_locations is None:
+ raise TypeError('{!r} is not a package'.format(package))
+ return module
def _normalize_path(path) -> str:
@@ -60,7 +60,7 @@ def _normalize_path(path) -> str:
parent, file_name = os.path.split(path)
if parent:
raise ValueError('{!r} must be only a file name'.format(path))
- return file_name
+ return file_name
def _get_resource_reader(
@@ -89,8 +89,8 @@ def open_binary(package: Package, resource: Resource) -> BinaryIO:
reader = _get_resource_reader(package)
if reader is not None:
return reader.open_resource(resource)
- absolute_package_path = os.path.abspath(
- package.__spec__.origin or 'non-existent file')
+ absolute_package_path = os.path.abspath(
+ package.__spec__.origin or 'non-existent file')
package_path = os.path.dirname(absolute_package_path)
full_path = os.path.join(package_path, resource)
try:
@@ -109,7 +109,7 @@ def open_binary(package: Package, resource: Resource) -> BinaryIO:
message = '{!r} resource not found in {!r}'.format(
resource, package_name)
raise FileNotFoundError(message)
- return BytesIO(data)
+ return BytesIO(data)
def open_text(package: Package,
@@ -117,8 +117,8 @@ def open_text(package: Package,
encoding: str = 'utf-8',
errors: str = 'strict') -> TextIO:
"""Return a file-like object opened for text reading of the resource."""
- return TextIOWrapper(
- open_binary(package, resource), encoding=encoding, errors=errors)
+ return TextIOWrapper(
+ open_binary(package, resource), encoding=encoding, errors=errors)
def read_binary(package: Package, resource: Resource) -> bytes:
@@ -140,16 +140,16 @@ def read_text(package: Package,
return fp.read()
-def files(package: Package) -> resources_abc.Traversable:
- """
- Get a Traversable resource from a package
- """
- return _common.from_package(_get_package(package))
-
-
-def path(
- package: Package, resource: Resource,
- ) -> 'ContextManager[Path]':
+def files(package: Package) -> resources_abc.Traversable:
+ """
+ Get a Traversable resource from a package
+ """
+ return _common.from_package(_get_package(package))
+
+
+def path(
+ package: Package, resource: Resource,
+ ) -> 'ContextManager[Path]':
"""A context manager providing a file path object to the resource.
If the resource does not already exist on its own on the file system,
@@ -158,25 +158,25 @@ def path(
raised if the file was deleted prior to the context manager
exiting).
"""
- reader = _get_resource_reader(_get_package(package))
- return (
- _path_from_reader(reader, resource)
- if reader else
- _common.as_file(files(package).joinpath(_normalize_path(resource)))
- )
+ reader = _get_resource_reader(_get_package(package))
+ return (
+ _path_from_reader(reader, resource)
+ if reader else
+ _common.as_file(files(package).joinpath(_normalize_path(resource)))
+ )
+
+
+@contextmanager
+def _path_from_reader(reader, resource):
+ norm_resource = _normalize_path(resource)
+ with suppress(FileNotFoundError):
+ yield Path(reader.resource_path(norm_resource))
+ return
+ opener_reader = reader.open_resource(norm_resource)
+ with _common._tempfile(opener_reader.read, suffix=norm_resource) as res:
+ yield res
-@contextmanager
-def _path_from_reader(reader, resource):
- norm_resource = _normalize_path(resource)
- with suppress(FileNotFoundError):
- yield Path(reader.resource_path(norm_resource))
- return
- opener_reader = reader.open_resource(norm_resource)
- with _common._tempfile(opener_reader.read, suffix=norm_resource) as res:
- yield res
-
-
def is_resource(package: Package, name: str) -> bool:
"""True if 'name' is a resource inside 'package'.
@@ -187,10 +187,10 @@ def is_resource(package: Package, name: str) -> bool:
reader = _get_resource_reader(package)
if reader is not None:
return reader.is_resource(name)
- package_contents = set(contents(package))
+ package_contents = set(contents(package))
if name not in package_contents:
return False
- return (_common.from_package(package) / name).is_file()
+ return (_common.from_package(package) / name).is_file()
def contents(package: Package) -> Iterable[str]:
@@ -205,11 +205,11 @@ def contents(package: Package) -> Iterable[str]:
if reader is not None:
return reader.contents()
# Is the package a namespace package? By definition, namespace packages
- # cannot have resources.
- namespace = (
- package.__spec__.origin is None or
- package.__spec__.origin == 'namespace'
- )
- if namespace or not package.__spec__.has_location:
+ # cannot have resources.
+ namespace = (
+ package.__spec__.origin is None or
+ package.__spec__.origin == 'namespace'
+ )
+ if namespace or not package.__spec__.has_location:
return ()
- return list(item.name for item in _common.from_package(package).iterdir())
+ return list(item.name for item in _common.from_package(package).iterdir())
diff --git a/contrib/tools/python3/src/Lib/importlib/util.py b/contrib/tools/python3/src/Lib/importlib/util.py
index 07a0f2db285..269a6fa930a 100644
--- a/contrib/tools/python3/src/Lib/importlib/util.py
+++ b/contrib/tools/python3/src/Lib/importlib/util.py
@@ -29,8 +29,8 @@ def resolve_name(name, package):
if not name.startswith('.'):
return name
elif not package:
- raise ImportError(f'no package specified for {repr(name)} '
- '(required for relative module names)')
+ raise ImportError(f'no package specified for {repr(name)} '
+ '(required for relative module names)')
level = 0
for character in name:
if character != '.':