(root)/
Python-3.12.0/
Lib/
importlib/
util.py
       1  """Utility code for constructing importers, etc."""
       2  from ._abc import Loader
       3  from ._bootstrap import module_from_spec
       4  from ._bootstrap import _resolve_name
       5  from ._bootstrap import spec_from_loader
       6  from ._bootstrap import _find_spec
       7  from ._bootstrap_external import MAGIC_NUMBER
       8  from ._bootstrap_external import _RAW_MAGIC_NUMBER
       9  from ._bootstrap_external import cache_from_source
      10  from ._bootstrap_external import decode_source
      11  from ._bootstrap_external import source_from_cache
      12  from ._bootstrap_external import spec_from_file_location
      13  
      14  import _imp
      15  import sys
      16  import types
      17  
      18  
      19  def source_hash(source_bytes):
      20      "Return the hash of *source_bytes* as used in hash-based pyc files."
      21      return _imp.source_hash(_RAW_MAGIC_NUMBER, source_bytes)
      22  
      23  
      24  def resolve_name(name, package):
      25      """Resolve a relative module name to an absolute one."""
      26      if not name.startswith('.'):
      27          return name
      28      elif not package:
      29          raise ImportError(f'no package specified for {repr(name)} '
      30                            '(required for relative module names)')
      31      level = 0
      32      for character in name:
      33          if character != '.':
      34              break
      35          level += 1
      36      return _resolve_name(name[level:], package, level)
      37  
      38  
      39  def _find_spec_from_path(name, path=None):
      40      """Return the spec for the specified module.
      41  
      42      First, sys.modules is checked to see if the module was already imported. If
      43      so, then sys.modules[name].__spec__ is returned. If that happens to be
      44      set to None, then ValueError is raised. If the module is not in
      45      sys.modules, then sys.meta_path is searched for a suitable spec with the
      46      value of 'path' given to the finders. None is returned if no spec could
      47      be found.
      48  
      49      Dotted names do not have their parent packages implicitly imported. You will
      50      most likely need to explicitly import all parent packages in the proper
      51      order for a submodule to get the correct spec.
      52  
      53      """
      54      if name not in sys.modules:
      55          return _find_spec(name, path)
      56      else:
      57          module = sys.modules[name]
      58          if module is None:
      59              return None
      60          try:
      61              spec = module.__spec__
      62          except AttributeError:
      63              raise ValueError(f'{name}.__spec__ is not set') from None
      64          else:
      65              if spec is None:
      66                  raise ValueError(f'{name}.__spec__ is None')
      67              return spec
      68  
      69  
      70  def find_spec(name, package=None):
      71      """Return the spec for the specified module.
      72  
      73      First, sys.modules is checked to see if the module was already imported. If
      74      so, then sys.modules[name].__spec__ is returned. If that happens to be
      75      set to None, then ValueError is raised. If the module is not in
      76      sys.modules, then sys.meta_path is searched for a suitable spec with the
      77      value of 'path' given to the finders. None is returned if no spec could
      78      be found.
      79  
      80      If the name is for submodule (contains a dot), the parent module is
      81      automatically imported.
      82  
      83      The name and package arguments work the same as importlib.import_module().
      84      In other words, relative module names (with leading dots) work.
      85  
      86      """
      87      fullname = resolve_name(name, package) if name.startswith('.') else name
      88      if fullname not in sys.modules:
      89          parent_name = fullname.rpartition('.')[0]
      90          if parent_name:
      91              parent = __import__(parent_name, fromlist=['__path__'])
      92              try:
      93                  parent_path = parent.__path__
      94              except AttributeError as e:
      95                  raise ModuleNotFoundError(
      96                      f"__path__ attribute not found on {parent_name!r} "
      97                      f"while trying to find {fullname!r}", name=fullname) from e
      98          else:
      99              parent_path = None
     100          return _find_spec(fullname, parent_path)
     101      else:
     102          module = sys.modules[fullname]
     103          if module is None:
     104              return None
     105          try:
     106              spec = module.__spec__
     107          except AttributeError:
     108              raise ValueError(f'{name}.__spec__ is not set') from None
     109          else:
     110              if spec is None:
     111                  raise ValueError(f'{name}.__spec__ is None')
     112              return spec
     113  
     114  
     115  # Normally we would use contextlib.contextmanager.  However, this module
     116  # is imported by runpy, which means we want to avoid any unnecessary
     117  # dependencies.  Thus we use a class.
     118  
     119  class ESC[4;38;5;81m_incompatible_extension_module_restrictions:
     120      """A context manager that can temporarily skip the compatibility check.
     121  
     122      NOTE: This function is meant to accommodate an unusual case; one
     123      which is likely to eventually go away.  There's is a pretty good
     124      chance this is not what you were looking for.
     125  
     126      WARNING: Using this function to disable the check can lead to
     127      unexpected behavior and even crashes.  It should only be used during
     128      extension module development.
     129  
     130      If "disable_check" is True then the compatibility check will not
     131      happen while the context manager is active.  Otherwise the check
     132      *will* happen.
     133  
     134      Normally, extensions that do not support multiple interpreters
     135      may not be imported in a subinterpreter.  That implies modules
     136      that do not implement multi-phase init or that explicitly of out.
     137  
     138      Likewise for modules import in a subinterpeter with its own GIL
     139      when the extension does not support a per-interpreter GIL.  This
     140      implies the module does not have a Py_mod_multiple_interpreters slot
     141      set to Py_MOD_PER_INTERPRETER_GIL_SUPPORTED.
     142  
     143      In both cases, this context manager may be used to temporarily
     144      disable the check for compatible extension modules.
     145  
     146      You can get the same effect as this function by implementing the
     147      basic interface of multi-phase init (PEP 489) and lying about
     148      support for mulitple interpreters (or per-interpreter GIL).
     149      """
     150  
     151      def __init__(self, *, disable_check):
     152          self.disable_check = bool(disable_check)
     153  
     154      def __enter__(self):
     155          self.old = _imp._override_multi_interp_extensions_check(self.override)
     156          return self
     157  
     158      def __exit__(self, *args):
     159          old = self.old
     160          del self.old
     161          _imp._override_multi_interp_extensions_check(old)
     162  
     163      @property
     164      def override(self):
     165          return -1 if self.disable_check else 1
     166  
     167  
     168  class ESC[4;38;5;81m_LazyModule(ESC[4;38;5;149mtypesESC[4;38;5;149m.ESC[4;38;5;149mModuleType):
     169  
     170      """A subclass of the module type which triggers loading upon attribute access."""
     171  
     172      def __getattribute__(self, attr):
     173          """Trigger the load of the module and return the attribute."""
     174          # All module metadata must be garnered from __spec__ in order to avoid
     175          # using mutated values.
     176          # Stop triggering this method.
     177          self.__class__ = types.ModuleType
     178          # Get the original name to make sure no object substitution occurred
     179          # in sys.modules.
     180          original_name = self.__spec__.name
     181          # Figure out exactly what attributes were mutated between the creation
     182          # of the module and now.
     183          attrs_then = self.__spec__.loader_state['__dict__']
     184          attrs_now = self.__dict__
     185          attrs_updated = {}
     186          for key, value in attrs_now.items():
     187              # Code that set the attribute may have kept a reference to the
     188              # assigned object, making identity more important than equality.
     189              if key not in attrs_then:
     190                  attrs_updated[key] = value
     191              elif id(attrs_now[key]) != id(attrs_then[key]):
     192                  attrs_updated[key] = value
     193          self.__spec__.loader.exec_module(self)
     194          # If exec_module() was used directly there is no guarantee the module
     195          # object was put into sys.modules.
     196          if original_name in sys.modules:
     197              if id(self) != id(sys.modules[original_name]):
     198                  raise ValueError(f"module object for {original_name!r} "
     199                                    "substituted in sys.modules during a lazy "
     200                                    "load")
     201          # Update after loading since that's what would happen in an eager
     202          # loading situation.
     203          self.__dict__.update(attrs_updated)
     204          return getattr(self, attr)
     205  
     206      def __delattr__(self, attr):
     207          """Trigger the load and then perform the deletion."""
     208          # To trigger the load and raise an exception if the attribute
     209          # doesn't exist.
     210          self.__getattribute__(attr)
     211          delattr(self, attr)
     212  
     213  
     214  class ESC[4;38;5;81mLazyLoader(ESC[4;38;5;149mLoader):
     215  
     216      """A loader that creates a module which defers loading until attribute access."""
     217  
     218      @staticmethod
     219      def __check_eager_loader(loader):
     220          if not hasattr(loader, 'exec_module'):
     221              raise TypeError('loader must define exec_module()')
     222  
     223      @classmethod
     224      def factory(cls, loader):
     225          """Construct a callable which returns the eager loader made lazy."""
     226          cls.__check_eager_loader(loader)
     227          return lambda *args, **kwargs: cls(loader(*args, **kwargs))
     228  
     229      def __init__(self, loader):
     230          self.__check_eager_loader(loader)
     231          self.loader = loader
     232  
     233      def create_module(self, spec):
     234          return self.loader.create_module(spec)
     235  
     236      def exec_module(self, module):
     237          """Make the module load lazily."""
     238          module.__spec__.loader = self.loader
     239          module.__loader__ = self.loader
     240          # Don't need to worry about deep-copying as trying to set an attribute
     241          # on an object would have triggered the load,
     242          # e.g. ``module.__spec__.loader = None`` would trigger a load from
     243          # trying to access module.__spec__.
     244          loader_state = {}
     245          loader_state['__dict__'] = module.__dict__.copy()
     246          loader_state['__class__'] = module.__class__
     247          module.__spec__.loader_state = loader_state
     248          module.__class__ = _LazyModule