utility code for constructing importers etc from abc import loader from bootstrap import modulefromspec from bootstrap import resolvename from bootstrap import specfromloader from bootstrap import findspec from bootstrapexternal import magicnumber from bootstrapexternal import rawmagicnumber from bootstrapexternal import cachefromsource from bootstrapexternal import decodesource from bootstrapexternal import sourcefromcache from bootstrapexternal import specfromfilelocation import imp import sys import types def sourcehashsourcebytes return the hash of sourcebytes as used in hashbased pyc files return imp sourcehashrawmagicnumber sourcebytes def resolvenamename package return the spec for the specified module first sys modules is checked to see if the module was already imported if so then sys modulesname spec is returned if that happens to be set to none then valueerror is raised if the module is not in sys modules then sys metapath is searched for a suitable spec with the value of path given to the finders none is returned if no spec could be found dotted names do not have their parent packages implicitly imported you will most likely need to explicitly import all parent packages in the proper order for a submodule to get the correct spec return the spec for the specified module first sys modules is checked to see if the module was already imported if so then sys modulesname spec is returned if that happens to be set to none then valueerror is raised if the module is not in sys modules then sys metapath is searched for a suitable spec with the value of path given to the finders none is returned if no spec could be found if the name is for submodule contains a dot the parent module is automatically imported the name and package arguments work the same as importlib importmodule in other words relative module names with leading dots work normally we would use contextlib contextmanager however this module is imported by runpy which means we want to avoid any unnecessary dependencies thus we use a class a context manager that can temporarily skip the compatibility check note this function is meant to accommodate an unusual case one which is likely to eventually go away there s is a pretty good chance this is not what you were looking for warning using this function to disable the check can lead to unexpected behavior and even crashes it should only be used during extension module development if disablecheck is true then the compatibility check will not happen while the context manager is active otherwise the check will happen normally extensions that do not support multiple interpreters may not be imported in a subinterpreter that implies modules that do not implement multiphase init or that explicitly of out likewise for modules import in a subinterpreter with its own gil when the extension does not support a perinterpreter gil this implies the module does not have a pymodmultipleinterpreters slot set to pymodperinterpretergilsupported in both cases this context manager may be used to temporarily disable the check for compatible extension modules you can get the same effect as this function by implementing the basic interface of multiphase init pep 489 and lying about support for mulitple interpreters or perinterpreter gil a subclass of the module type which triggers loading upon attribute access def getattributeself attr all module metadata must be garnered from spec in order to avoid using mutated values stop triggering this method get the original name to make sure no object substitution occurred in sys modules figure out exactly what attributes were mutated between the creation of the module and now code that set the attribute may have kept a reference to the assigned object making identity more important than equality if execmodule was used directly there is no guarantee the module object was put into sys modules update after loading since that s what would happen in an eager loading situation trigger the load and then perform the deletion to trigger the load and raise an exception if the attribute doesn t exist self getattributeattr delattrself attr class lazyloaderloader construct a callable which returns the eager loader made lazy cls checkeagerloaderloader return lambda args kwargs clsloaderargs kwargs def initself loader self checkeagerloaderloader self loader loader def createmoduleself spec return self loader createmodulespec def execmoduleself module don t need to worry about deepcopying as trying to set an attribute on an object would have triggered the load e g module spec loader none would trigger a load from trying to access module spec resolve a relative module name to an absolute one return the spec for the specified module first sys modules is checked to see if the module was already imported if so then sys modules name __spec__ is returned if that happens to be set to none then valueerror is raised if the module is not in sys modules then sys meta_path is searched for a suitable spec with the value of path given to the finders none is returned if no spec could be found dotted names do not have their parent packages implicitly imported you will most likely need to explicitly import all parent packages in the proper order for a submodule to get the correct spec return the spec for the specified module first sys modules is checked to see if the module was already imported if so then sys modules name __spec__ is returned if that happens to be set to none then valueerror is raised if the module is not in sys modules then sys meta_path is searched for a suitable spec with the value of path given to the finders none is returned if no spec could be found if the name is for submodule contains a dot the parent module is automatically imported the name and package arguments work the same as importlib import_module in other words relative module names with leading dots work normally we would use contextlib contextmanager however this module is imported by runpy which means we want to avoid any unnecessary dependencies thus we use a class a context manager that can temporarily skip the compatibility check note this function is meant to accommodate an unusual case one which is likely to eventually go away there s is a pretty good chance this is not what you were looking for warning using this function to disable the check can lead to unexpected behavior and even crashes it should only be used during extension module development if disable_check is true then the compatibility check will not happen while the context manager is active otherwise the check will happen normally extensions that do not support multiple interpreters may not be imported in a subinterpreter that implies modules that do not implement multi phase init or that explicitly of out likewise for modules import in a subinterpreter with its own gil when the extension does not support a per interpreter gil this implies the module does not have a py_mod_multiple_interpreters slot set to py_mod_per_interpreter_gil_supported in both cases this context manager may be used to temporarily disable the check for compatible extension modules you can get the same effect as this function by implementing the basic interface of multi phase init pep 489 and lying about support for mulitple interpreters or per interpreter gil a subclass of the module type which triggers loading upon attribute access trigger the load of the module and return the attribute all module metadata must be garnered from __spec__ in order to avoid using mutated values stop triggering this method get the original name to make sure no object substitution occurred in sys modules figure out exactly what attributes were mutated between the creation of the module and now code that set the attribute may have kept a reference to the assigned object making identity more important than equality if exec_module was used directly there is no guarantee the module object was put into sys modules update after loading since that s what would happen in an eager loading situation trigger the load and then perform the deletion to trigger the load and raise an exception if the attribute doesn t exist a loader that creates a module which defers loading until attribute access construct a callable which returns the eager loader made lazy make the module load lazily don t need to worry about deep copying as trying to set an attribute on an object would have triggered the load e g module __spec__ loader none would trigger a load from trying to access module __spec__
from ._abc import Loader
from ._bootstrap import module_from_spec
from ._bootstrap import _resolve_name
from ._bootstrap import spec_from_loader
from ._bootstrap import _find_spec
from ._bootstrap_external import MAGIC_NUMBER
from ._bootstrap_external import _RAW_MAGIC_NUMBER
from ._bootstrap_external import cache_from_source
from ._bootstrap_external import decode_source
from ._bootstrap_external import source_from_cache
from ._bootstrap_external import spec_from_file_location

import _imp
import sys
import types


def source_hash(source_bytes):
    "Return the hash of *source_bytes* as used in hash-based pyc files."
    return _imp.source_hash(_RAW_MAGIC_NUMBER, source_bytes)


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)')
    level = 0
    for character in name:
        if character != '.':
            break
        level += 1
    return _resolve_name(name[level:], package, level)


def _find_spec_from_path(name, path=None):
    
    if name not in sys.modules:
        return _find_spec(name, path)
    else:
        module = sys.modules[name]
        if module is None:
            return None
        try:
            spec = module.__spec__
        except AttributeError:
            raise ValueError(f'{name}.__spec__ is not set') from None
        else:
            if spec is None:
                raise ValueError(f'{name}.__spec__ is None')
            return spec


def find_spec(name, package=None):
    
    fullname = resolve_name(name, package) if name.startswith('.') else name
    if fullname not in sys.modules:
        parent_name = fullname.rpartition('.')[0]
        if parent_name:
            parent = __import__(parent_name, fromlist=['__path__'])
            try:
                parent_path = parent.__path__
            except AttributeError as e:
                raise ModuleNotFoundError(
                    f"__path__ attribute not found on {parent_name!r} "
                    f"while trying to find {fullname!r}", name=fullname) from e
        else:
            parent_path = None
        return _find_spec(fullname, parent_path)
    else:
        module = sys.modules[fullname]
        if module is None:
            return None
        try:
            spec = module.__spec__
        except AttributeError:
            raise ValueError(f'{name}.__spec__ is not set') from None
        else:
            if spec is None:
                raise ValueError(f'{name}.__spec__ is None')
            return spec






class _incompatible_extension_module_restrictions:
    

    def __init__(self, *, disable_check):
        self.disable_check = bool(disable_check)

    def __enter__(self):
        self.old = _imp._override_multi_interp_extensions_check(self.override)
        return self

    def __exit__(self, *args):
        old = self.old
        del self.old
        _imp._override_multi_interp_extensions_check(old)

    @property
    def override(self):
        return -1 if self.disable_check else 1


class _LazyModule(types.ModuleType):

    

    def __getattribute__(self, attr):
        
        
        
        
        self.__class__ = types.ModuleType
        
        
        original_name = self.__spec__.name
        
        
        attrs_then = self.__spec__.loader_state['__dict__']
        attrs_now = self.__dict__
        attrs_updated = {}
        for key, value in attrs_now.items():
            
            
            if key not in attrs_then:
                attrs_updated[key] = value
            elif id(attrs_now[key]) != id(attrs_then[key]):
                attrs_updated[key] = value
        self.__spec__.loader.exec_module(self)
        
        
        if original_name in sys.modules:
            if id(self) != id(sys.modules[original_name]):
                raise ValueError(f"module object for {original_name!r} "
                                  "substituted in sys.modules during a lazy "
                                  "load")
        
        
        self.__dict__.update(attrs_updated)
        return getattr(self, attr)

    def __delattr__(self, attr):
        
        
        
        self.__getattribute__(attr)
        delattr(self, attr)


class LazyLoader(Loader):

    

    @staticmethod
    def __check_eager_loader(loader):
        if not hasattr(loader, 'exec_module'):
            raise TypeError('loader must define exec_module()')

    @classmethod
    def factory(cls, loader):
        
        cls.__check_eager_loader(loader)
        return lambda *args, **kwargs: cls(loader(*args, **kwargs))

    def __init__(self, loader):
        self.__check_eager_loader(loader)
        self.loader = loader

    def create_module(self, spec):
        return self.loader.create_module(spec)

    def exec_module(self, module):
        
        module.__spec__.loader = self.loader
        module.__loader__ = self.loader
        
        
        
        
        loader_state = {}
        loader_state['__dict__'] = module.__dict__.copy()
        loader_state['__class__'] = module.__class__
        module.__spec__.loader_state = loader_state
        module.__class__ = _LazyModule