annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/importlib/util.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
rev   line source
jpayne@68 1 """Utility code for constructing importers, etc."""
jpayne@68 2 from . import abc
jpayne@68 3 from ._bootstrap import module_from_spec
jpayne@68 4 from ._bootstrap import _resolve_name
jpayne@68 5 from ._bootstrap import spec_from_loader
jpayne@68 6 from ._bootstrap import _find_spec
jpayne@68 7 from ._bootstrap_external import MAGIC_NUMBER
jpayne@68 8 from ._bootstrap_external import _RAW_MAGIC_NUMBER
jpayne@68 9 from ._bootstrap_external import cache_from_source
jpayne@68 10 from ._bootstrap_external import decode_source
jpayne@68 11 from ._bootstrap_external import source_from_cache
jpayne@68 12 from ._bootstrap_external import spec_from_file_location
jpayne@68 13
jpayne@68 14 from contextlib import contextmanager
jpayne@68 15 import _imp
jpayne@68 16 import functools
jpayne@68 17 import sys
jpayne@68 18 import types
jpayne@68 19 import warnings
jpayne@68 20
jpayne@68 21
jpayne@68 22 def source_hash(source_bytes):
jpayne@68 23 "Return the hash of *source_bytes* as used in hash-based pyc files."
jpayne@68 24 return _imp.source_hash(_RAW_MAGIC_NUMBER, source_bytes)
jpayne@68 25
jpayne@68 26
jpayne@68 27 def resolve_name(name, package):
jpayne@68 28 """Resolve a relative module name to an absolute one."""
jpayne@68 29 if not name.startswith('.'):
jpayne@68 30 return name
jpayne@68 31 elif not package:
jpayne@68 32 raise ValueError(f'no package specified for {repr(name)} '
jpayne@68 33 '(required for relative module names)')
jpayne@68 34 level = 0
jpayne@68 35 for character in name:
jpayne@68 36 if character != '.':
jpayne@68 37 break
jpayne@68 38 level += 1
jpayne@68 39 return _resolve_name(name[level:], package, level)
jpayne@68 40
jpayne@68 41
jpayne@68 42 def _find_spec_from_path(name, path=None):
jpayne@68 43 """Return the spec for the specified module.
jpayne@68 44
jpayne@68 45 First, sys.modules is checked to see if the module was already imported. If
jpayne@68 46 so, then sys.modules[name].__spec__ is returned. If that happens to be
jpayne@68 47 set to None, then ValueError is raised. If the module is not in
jpayne@68 48 sys.modules, then sys.meta_path is searched for a suitable spec with the
jpayne@68 49 value of 'path' given to the finders. None is returned if no spec could
jpayne@68 50 be found.
jpayne@68 51
jpayne@68 52 Dotted names do not have their parent packages implicitly imported. You will
jpayne@68 53 most likely need to explicitly import all parent packages in the proper
jpayne@68 54 order for a submodule to get the correct spec.
jpayne@68 55
jpayne@68 56 """
jpayne@68 57 if name not in sys.modules:
jpayne@68 58 return _find_spec(name, path)
jpayne@68 59 else:
jpayne@68 60 module = sys.modules[name]
jpayne@68 61 if module is None:
jpayne@68 62 return None
jpayne@68 63 try:
jpayne@68 64 spec = module.__spec__
jpayne@68 65 except AttributeError:
jpayne@68 66 raise ValueError('{}.__spec__ is not set'.format(name)) from None
jpayne@68 67 else:
jpayne@68 68 if spec is None:
jpayne@68 69 raise ValueError('{}.__spec__ is None'.format(name))
jpayne@68 70 return spec
jpayne@68 71
jpayne@68 72
jpayne@68 73 def find_spec(name, package=None):
jpayne@68 74 """Return the spec for the specified module.
jpayne@68 75
jpayne@68 76 First, sys.modules is checked to see if the module was already imported. If
jpayne@68 77 so, then sys.modules[name].__spec__ is returned. If that happens to be
jpayne@68 78 set to None, then ValueError is raised. If the module is not in
jpayne@68 79 sys.modules, then sys.meta_path is searched for a suitable spec with the
jpayne@68 80 value of 'path' given to the finders. None is returned if no spec could
jpayne@68 81 be found.
jpayne@68 82
jpayne@68 83 If the name is for submodule (contains a dot), the parent module is
jpayne@68 84 automatically imported.
jpayne@68 85
jpayne@68 86 The name and package arguments work the same as importlib.import_module().
jpayne@68 87 In other words, relative module names (with leading dots) work.
jpayne@68 88
jpayne@68 89 """
jpayne@68 90 fullname = resolve_name(name, package) if name.startswith('.') else name
jpayne@68 91 if fullname not in sys.modules:
jpayne@68 92 parent_name = fullname.rpartition('.')[0]
jpayne@68 93 if parent_name:
jpayne@68 94 parent = __import__(parent_name, fromlist=['__path__'])
jpayne@68 95 try:
jpayne@68 96 parent_path = parent.__path__
jpayne@68 97 except AttributeError as e:
jpayne@68 98 raise ModuleNotFoundError(
jpayne@68 99 f"__path__ attribute not found on {parent_name!r} "
jpayne@68 100 f"while trying to find {fullname!r}", name=fullname) from e
jpayne@68 101 else:
jpayne@68 102 parent_path = None
jpayne@68 103 return _find_spec(fullname, parent_path)
jpayne@68 104 else:
jpayne@68 105 module = sys.modules[fullname]
jpayne@68 106 if module is None:
jpayne@68 107 return None
jpayne@68 108 try:
jpayne@68 109 spec = module.__spec__
jpayne@68 110 except AttributeError:
jpayne@68 111 raise ValueError('{}.__spec__ is not set'.format(name)) from None
jpayne@68 112 else:
jpayne@68 113 if spec is None:
jpayne@68 114 raise ValueError('{}.__spec__ is None'.format(name))
jpayne@68 115 return spec
jpayne@68 116
jpayne@68 117
jpayne@68 118 @contextmanager
jpayne@68 119 def _module_to_load(name):
jpayne@68 120 is_reload = name in sys.modules
jpayne@68 121
jpayne@68 122 module = sys.modules.get(name)
jpayne@68 123 if not is_reload:
jpayne@68 124 # This must be done before open() is called as the 'io' module
jpayne@68 125 # implicitly imports 'locale' and would otherwise trigger an
jpayne@68 126 # infinite loop.
jpayne@68 127 module = type(sys)(name)
jpayne@68 128 # This must be done before putting the module in sys.modules
jpayne@68 129 # (otherwise an optimization shortcut in import.c becomes wrong)
jpayne@68 130 module.__initializing__ = True
jpayne@68 131 sys.modules[name] = module
jpayne@68 132 try:
jpayne@68 133 yield module
jpayne@68 134 except Exception:
jpayne@68 135 if not is_reload:
jpayne@68 136 try:
jpayne@68 137 del sys.modules[name]
jpayne@68 138 except KeyError:
jpayne@68 139 pass
jpayne@68 140 finally:
jpayne@68 141 module.__initializing__ = False
jpayne@68 142
jpayne@68 143
jpayne@68 144 def set_package(fxn):
jpayne@68 145 """Set __package__ on the returned module.
jpayne@68 146
jpayne@68 147 This function is deprecated.
jpayne@68 148
jpayne@68 149 """
jpayne@68 150 @functools.wraps(fxn)
jpayne@68 151 def set_package_wrapper(*args, **kwargs):
jpayne@68 152 warnings.warn('The import system now takes care of this automatically.',
jpayne@68 153 DeprecationWarning, stacklevel=2)
jpayne@68 154 module = fxn(*args, **kwargs)
jpayne@68 155 if getattr(module, '__package__', None) is None:
jpayne@68 156 module.__package__ = module.__name__
jpayne@68 157 if not hasattr(module, '__path__'):
jpayne@68 158 module.__package__ = module.__package__.rpartition('.')[0]
jpayne@68 159 return module
jpayne@68 160 return set_package_wrapper
jpayne@68 161
jpayne@68 162
jpayne@68 163 def set_loader(fxn):
jpayne@68 164 """Set __loader__ on the returned module.
jpayne@68 165
jpayne@68 166 This function is deprecated.
jpayne@68 167
jpayne@68 168 """
jpayne@68 169 @functools.wraps(fxn)
jpayne@68 170 def set_loader_wrapper(self, *args, **kwargs):
jpayne@68 171 warnings.warn('The import system now takes care of this automatically.',
jpayne@68 172 DeprecationWarning, stacklevel=2)
jpayne@68 173 module = fxn(self, *args, **kwargs)
jpayne@68 174 if getattr(module, '__loader__', None) is None:
jpayne@68 175 module.__loader__ = self
jpayne@68 176 return module
jpayne@68 177 return set_loader_wrapper
jpayne@68 178
jpayne@68 179
jpayne@68 180 def module_for_loader(fxn):
jpayne@68 181 """Decorator to handle selecting the proper module for loaders.
jpayne@68 182
jpayne@68 183 The decorated function is passed the module to use instead of the module
jpayne@68 184 name. The module passed in to the function is either from sys.modules if
jpayne@68 185 it already exists or is a new module. If the module is new, then __name__
jpayne@68 186 is set the first argument to the method, __loader__ is set to self, and
jpayne@68 187 __package__ is set accordingly (if self.is_package() is defined) will be set
jpayne@68 188 before it is passed to the decorated function (if self.is_package() does
jpayne@68 189 not work for the module it will be set post-load).
jpayne@68 190
jpayne@68 191 If an exception is raised and the decorator created the module it is
jpayne@68 192 subsequently removed from sys.modules.
jpayne@68 193
jpayne@68 194 The decorator assumes that the decorated function takes the module name as
jpayne@68 195 the second argument.
jpayne@68 196
jpayne@68 197 """
jpayne@68 198 warnings.warn('The import system now takes care of this automatically.',
jpayne@68 199 DeprecationWarning, stacklevel=2)
jpayne@68 200 @functools.wraps(fxn)
jpayne@68 201 def module_for_loader_wrapper(self, fullname, *args, **kwargs):
jpayne@68 202 with _module_to_load(fullname) as module:
jpayne@68 203 module.__loader__ = self
jpayne@68 204 try:
jpayne@68 205 is_package = self.is_package(fullname)
jpayne@68 206 except (ImportError, AttributeError):
jpayne@68 207 pass
jpayne@68 208 else:
jpayne@68 209 if is_package:
jpayne@68 210 module.__package__ = fullname
jpayne@68 211 else:
jpayne@68 212 module.__package__ = fullname.rpartition('.')[0]
jpayne@68 213 # If __package__ was not set above, __import__() will do it later.
jpayne@68 214 return fxn(self, module, *args, **kwargs)
jpayne@68 215
jpayne@68 216 return module_for_loader_wrapper
jpayne@68 217
jpayne@68 218
jpayne@68 219 class _LazyModule(types.ModuleType):
jpayne@68 220
jpayne@68 221 """A subclass of the module type which triggers loading upon attribute access."""
jpayne@68 222
jpayne@68 223 def __getattribute__(self, attr):
jpayne@68 224 """Trigger the load of the module and return the attribute."""
jpayne@68 225 # All module metadata must be garnered from __spec__ in order to avoid
jpayne@68 226 # using mutated values.
jpayne@68 227 # Stop triggering this method.
jpayne@68 228 self.__class__ = types.ModuleType
jpayne@68 229 # Get the original name to make sure no object substitution occurred
jpayne@68 230 # in sys.modules.
jpayne@68 231 original_name = self.__spec__.name
jpayne@68 232 # Figure out exactly what attributes were mutated between the creation
jpayne@68 233 # of the module and now.
jpayne@68 234 attrs_then = self.__spec__.loader_state['__dict__']
jpayne@68 235 original_type = self.__spec__.loader_state['__class__']
jpayne@68 236 attrs_now = self.__dict__
jpayne@68 237 attrs_updated = {}
jpayne@68 238 for key, value in attrs_now.items():
jpayne@68 239 # Code that set the attribute may have kept a reference to the
jpayne@68 240 # assigned object, making identity more important than equality.
jpayne@68 241 if key not in attrs_then:
jpayne@68 242 attrs_updated[key] = value
jpayne@68 243 elif id(attrs_now[key]) != id(attrs_then[key]):
jpayne@68 244 attrs_updated[key] = value
jpayne@68 245 self.__spec__.loader.exec_module(self)
jpayne@68 246 # If exec_module() was used directly there is no guarantee the module
jpayne@68 247 # object was put into sys.modules.
jpayne@68 248 if original_name in sys.modules:
jpayne@68 249 if id(self) != id(sys.modules[original_name]):
jpayne@68 250 raise ValueError(f"module object for {original_name!r} "
jpayne@68 251 "substituted in sys.modules during a lazy "
jpayne@68 252 "load")
jpayne@68 253 # Update after loading since that's what would happen in an eager
jpayne@68 254 # loading situation.
jpayne@68 255 self.__dict__.update(attrs_updated)
jpayne@68 256 return getattr(self, attr)
jpayne@68 257
jpayne@68 258 def __delattr__(self, attr):
jpayne@68 259 """Trigger the load and then perform the deletion."""
jpayne@68 260 # To trigger the load and raise an exception if the attribute
jpayne@68 261 # doesn't exist.
jpayne@68 262 self.__getattribute__(attr)
jpayne@68 263 delattr(self, attr)
jpayne@68 264
jpayne@68 265
jpayne@68 266 class LazyLoader(abc.Loader):
jpayne@68 267
jpayne@68 268 """A loader that creates a module which defers loading until attribute access."""
jpayne@68 269
jpayne@68 270 @staticmethod
jpayne@68 271 def __check_eager_loader(loader):
jpayne@68 272 if not hasattr(loader, 'exec_module'):
jpayne@68 273 raise TypeError('loader must define exec_module()')
jpayne@68 274
jpayne@68 275 @classmethod
jpayne@68 276 def factory(cls, loader):
jpayne@68 277 """Construct a callable which returns the eager loader made lazy."""
jpayne@68 278 cls.__check_eager_loader(loader)
jpayne@68 279 return lambda *args, **kwargs: cls(loader(*args, **kwargs))
jpayne@68 280
jpayne@68 281 def __init__(self, loader):
jpayne@68 282 self.__check_eager_loader(loader)
jpayne@68 283 self.loader = loader
jpayne@68 284
jpayne@68 285 def create_module(self, spec):
jpayne@68 286 return self.loader.create_module(spec)
jpayne@68 287
jpayne@68 288 def exec_module(self, module):
jpayne@68 289 """Make the module load lazily."""
jpayne@68 290 module.__spec__.loader = self.loader
jpayne@68 291 module.__loader__ = self.loader
jpayne@68 292 # Don't need to worry about deep-copying as trying to set an attribute
jpayne@68 293 # on an object would have triggered the load,
jpayne@68 294 # e.g. ``module.__spec__.loader = None`` would trigger a load from
jpayne@68 295 # trying to access module.__spec__.
jpayne@68 296 loader_state = {}
jpayne@68 297 loader_state['__dict__'] = module.__dict__.copy()
jpayne@68 298 loader_state['__class__'] = module.__class__
jpayne@68 299 module.__spec__.loader_state = loader_state
jpayne@68 300 module.__class__ = _LazyModule