Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
work:2024-49 [2024/12/14 00:58] – magsilva | work:2024-49 [2024/12/14 01:49] (current) – magsilva | ||
---|---|---|---|
Line 8: | Line 8: | ||
===== Pesquisa ===== | ===== Pesquisa ===== | ||
* Correção automática de programas | * Correção automática de programas | ||
- | * Um problema que tive recentemente foi quanto a redefinição de módulos e funções da biblioteca básica do Python. | + | * Um problema que tive recentemente foi quanto a redefinição de módulos |
* Normalmente, | * Normalmente, | ||
- | * Mais especificamente, | + | * Mais especificamente, |
* Existe mais um adendo de que, por ser para uma disciplina introdutório, | * Existe mais um adendo de que, por ser para uma disciplina introdutório, | ||
* Em tese, seria suficiente criar um arquivo ''' | * Em tese, seria suficiente criar um arquivo ''' | ||
* Assim, resolvi ser criativo e verificar como ocorre o carregamento dos módulos e se existia algo que eu pudesse trabalhar naquele nível. Eis que os problemas surgem :-) | * Assim, resolvi ser criativo e verificar como ocorre o carregamento dos módulos e se existia algo que eu pudesse trabalhar naquele nível. Eis que os problemas surgem :-) | ||
- | * | + | * A implementação padrão do Python, CPython, está disponível de forma aberta em https:// |
+ | * Um desenvolvedor criou um módulo [[https:// | ||
+ | * Só para complementar, | ||
+ | * A importação de pacotes do Python até que é simples de entender, sob certo aspecto. Existe uma classe para encontrar módulos e outra classe para carregar módulos. | ||
+ | * Para encontrar os módulos, são utilizadas as classes BuiltinImporter, | ||
+ | * As classes a serem utilizadas estão definidas na variável '' | ||
+ | * Entendido isso, bastaria criar uma nova classe Finder e colocá-la no início do caminho. | ||
+ | * Bom, nem tudo são flores. Por algum motivo muito estranho, **alguns** módulos builtins do Python não podem ser sobreescritos por esse mecanismo. Mais especificamente o módulo '' | ||
+ | * Minha primeira abordagem foi alterar o código da importação para que não ocorresse mais esse impedimento de sobreescrever elementos builtins. Isso funcionou! Colocarei o código a seguir, que sobreescreve o '' | ||
+ | <code language=" | ||
+ | from typing import Any, Callable, Dict, List, Optional | ||
+ | import importlib | ||
+ | import sys | ||
+ | import importlib.abc | ||
+ | import _imp | ||
+ | |||
+ | def _verbose_message(message, | ||
+ | """ | ||
+ | if sys.flags.verbose >= verbosity: | ||
+ | if not message.startswith(('#', | ||
+ | message = '# ' + message | ||
+ | print(message.format(*args), | ||
+ | |||
+ | |||
+ | def _find_spec(name, | ||
+ | """ | ||
+ | meta_path = sys.meta_path | ||
+ | if meta_path is None: | ||
+ | # PyImport_Cleanup() is running or has been called. | ||
+ | raise ImportError(" | ||
+ | |||
+ | if not meta_path: | ||
+ | _warnings.warn(' | ||
+ | |||
+ | # We check sys.modules here for the reload case. While a passed-in | ||
+ | # target will usually indicate a reload there is no guarantee, whereas | ||
+ | # sys.modules provides one. | ||
+ | is_reload = name in sys.modules | ||
+ | for finder in meta_path: | ||
+ | try: | ||
+ | find_spec = finder.find_spec | ||
+ | except AttributeError: | ||
+ | | ||
+ | else: | ||
+ | spec = find_spec(name, | ||
+ | if spec is not None: | ||
+ | # The parent import may have already imported this module. | ||
+ | if not is_reload and name in sys.modules: | ||
+ | module = sys.modules[name] | ||
+ | try: | ||
+ | __spec__ = module.__spec__ | ||
+ | except AttributeError: | ||
+ | # We use the found spec since that is the one that | ||
+ | # we would have used if the parent module hadn' | ||
+ | # beaten us to the punch. | ||
+ | return spec | ||
+ | else: | ||
+ | if __spec__ is None: | ||
+ | return spec | ||
+ | else: | ||
+ | return __spec__ | ||
+ | else: | ||
+ | return spec | ||
+ | else: | ||
+ | return None | ||
+ | |||
+ | def _sanity_check(name, | ||
+ | """ | ||
+ | if not isinstance(name, | ||
+ | raise TypeError(f' | ||
+ | if level < 0: | ||
+ | raise ValueError(' | ||
+ | if level > 0: | ||
+ | if not isinstance(package, | ||
+ | raise TypeError(' | ||
+ | elif not package: | ||
+ | raise ImportError(' | ||
+ | if not name and level == 0: | ||
+ | raise ValueError(' | ||
+ | |||
+ | def _resolve_name(name, | ||
+ | """ | ||
+ | bits = package.rsplit(' | ||
+ | if len(bits) < level: | ||
+ | raise ImportError(' | ||
+ | base = bits[0] | ||
+ | return f' | ||
+ | |||
+ | def _init_module_attrs(spec, | ||
+ | # The passed-in module may be not support attribute assignment, | ||
+ | # in which case we simply don't set the attributes. | ||
+ | # __name__ | ||
+ | if (override or getattr(module, | ||
+ | try: | ||
+ | module.__name__ = spec.name | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | # __loader__ | ||
+ | if override or getattr(module, | ||
+ | loader = spec.loader | ||
+ | if loader is None: | ||
+ | # A backward compatibility hack. | ||
+ | if spec.submodule_search_locations is not None: | ||
+ | if _bootstrap_external is None: | ||
+ | raise NotImplementedError | ||
+ | NamespaceLoader = _bootstrap_external.NamespaceLoader | ||
+ | |||
+ | loader = NamespaceLoader.__new__(NamespaceLoader) | ||
+ | loader._path = spec.submodule_search_locations | ||
+ | spec.loader = loader | ||
+ | # While the docs say that module.__file__ is not set for | ||
+ | # built-in modules, and the code below will avoid setting it if | ||
+ | # spec.has_location is false, this is incorrect for namespace | ||
+ | # packages. | ||
+ | # __spec__.origin is None, and thus their module.__file__ | ||
+ | # should also be None for consistency. | ||
+ | # this is the best place to ensure this consistency. | ||
+ | # | ||
+ | # See # https:// | ||
+ | # and bpo-32305 | ||
+ | module.__file__ = None | ||
+ | try: | ||
+ | module.__loader__ = loader | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | # __package__ | ||
+ | if override or getattr(module, | ||
+ | try: | ||
+ | module.__package__ = spec.parent | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | # __spec__ | ||
+ | try: | ||
+ | module.__spec__ = spec | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | # __path__ | ||
+ | if override or getattr(module, | ||
+ | if spec.submodule_search_locations is not None: | ||
+ | # XXX We should extend __path__ if it's already a list. | ||
+ | try: | ||
+ | module.__path__ = spec.submodule_search_locations | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | # __file__/ | ||
+ | if spec.has_location: | ||
+ | if override or getattr(module, | ||
+ | try: | ||
+ | module.__file__ = spec.origin | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | |||
+ | if override or getattr(module, | ||
+ | if spec.cached is not None: | ||
+ | try: | ||
+ | module.__cached__ = spec.cached | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | return module | ||
+ | |||
+ | def module_from_spec(spec): | ||
+ | """ | ||
+ | # Typically loaders will not implement create_module(). | ||
+ | module = None | ||
+ | if hasattr(spec.loader, | ||
+ | # If create_module() returns `None` then it means default | ||
+ | # module creation should be used. | ||
+ | module = spec.loader.create_module(spec) | ||
+ | elif hasattr(spec.loader, | ||
+ | raise ImportError(' | ||
+ | 'must also define create_module()' | ||
+ | if module is None: | ||
+ | module = _new_module(spec.name) | ||
+ | _init_module_attrs(spec, | ||
+ | return module | ||
+ | |||
+ | def _load_unlocked(spec): | ||
+ | # A helper for direct use by the import system. | ||
+ | if spec.loader is not None: | ||
+ | # Not a namespace package. | ||
+ | if not hasattr(spec.loader, | ||
+ | msg = (f" | ||
+ | " | ||
+ | _warnings.warn(msg, | ||
+ | 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(' | ||
+ | # 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(' | ||
+ | finally: | ||
+ | spec._initializing = False | ||
+ | |||
+ | return module | ||
+ | |||
+ | def _find_and_load_unlocked(name, | ||
+ | path = None | ||
+ | parent = name.rpartition(' | ||
+ | parent_spec = None | ||
+ | if parent: | ||
+ | if parent not in sys.modules: | ||
+ | _call_with_frames_removed(import_, | ||
+ | # Crazy side-effects! | ||
+ | if name in sys.modules: | ||
+ | return sys.modules[name] | ||
+ | parent_module = sys.modules[parent] | ||
+ | try: | ||
+ | path = parent_module.__path__ | ||
+ | except AttributeError: | ||
+ | msg = f' | ||
+ | raise ModuleNotFoundError(msg, | ||
+ | parent_spec = parent_module.__spec__ | ||
+ | child = name.rpartition(' | ||
+ | spec = _find_spec(name, | ||
+ | if spec is None: | ||
+ | raise ModuleNotFoundError(f' | ||
+ | else: | ||
+ | if parent_spec: | ||
+ | # Temporarily add child we are currently importing to parent' | ||
+ | # _uninitialized_submodules for circular import tracking. | ||
+ | parent_spec._uninitialized_submodules.append(child) | ||
+ | try: | ||
+ | module = _load_unlocked(spec) | ||
+ | finally: | ||
+ | if parent_spec: | ||
+ | parent_spec._uninitialized_submodules.pop() | ||
+ | if parent: | ||
+ | # Set the module as an attribute on its parent. | ||
+ | parent_module = sys.modules[parent] | ||
+ | try: | ||
+ | setattr(parent_module, | ||
+ | except AttributeError: | ||
+ | msg = f" | ||
+ | _warnings.warn(msg, | ||
+ | return module | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | def _find_and_load(name, | ||
+ | """ | ||
+ | |||
+ | # Optimization: | ||
+ | # already exists in sys.modules and is fully initialized. | ||
+ | # module = sys.modules.get(name, | ||
+ | return _find_and_load_unlocked(name, | ||
+ | |||
+ | |||
+ | if module is None: | ||
+ | message = f' | ||
+ | raise ModuleNotFoundError(message, | ||
+ | |||
+ | return module | ||
+ | |||
+ | |||
+ | def _gcd_import(name, | ||
+ | """ | ||
+ | being made from, and the level adjustment. | ||
+ | |||
+ | This function represents the greatest common denominator of functionality | ||
+ | between import_module and __import__. This includes setting __package__ if | ||
+ | the loader did not. | ||
+ | |||
+ | """ | ||
+ | _sanity_check(name, | ||
+ | if level > 0: | ||
+ | name = _resolve_name(name, | ||
+ | return _find_and_load(name, | ||
+ | |||
+ | |||
+ | def __import__(name, | ||
+ | """ | ||
+ | |||
+ | The ' | ||
+ | to handle relative imports. The ' | ||
+ | ' | ||
+ | being imported (e.g. ``from module import < | ||
+ | argument represents the package location to import from in a relative | ||
+ | import (e.g. ``from ..pkg import mod`` would have a ' | ||
+ | |||
+ | """ | ||
+ | if level == 0: | ||
+ | module = _gcd_import(name) | ||
+ | else: | ||
+ | globals_ = globals if globals is not None else {} | ||
+ | package = _calc___package__(globals_) | ||
+ | module = _gcd_import(name, | ||
+ | if not fromlist: | ||
+ | # Return up to the first dot in ' | ||
+ | # that ' | ||
+ | if level == 0: | ||
+ | return _gcd_import(name.partition(' | ||
+ | elif not name: | ||
+ | return module | ||
+ | else: | ||
+ | # Figure out where to slice the module' | ||
+ | # in ' | ||
+ | cut_off = len(name) - len(name.partition(' | ||
+ | # Slice end needs to be positive to alleviate need to special-case | ||
+ | # when ``' | ||
+ | return sys.modules[module.__name__[: | ||
+ | elif hasattr(module, | ||
+ | return _handle_fromlist(module, | ||
+ | else: | ||
+ | return module | ||
+ | |||
+ | |||
+ | class ModuleSpec: | ||
+ | """ | ||
+ | |||
+ | A module' | ||
+ | data associated with the module, including source, use the spec' | ||
+ | loader. | ||
+ | |||
+ | `name` is the absolute name of the module. | ||
+ | to use when loading the module. | ||
+ | package the module is in. The parent is derived from the name. | ||
+ | |||
+ | `is_package` determines if the module is considered a package or | ||
+ | not. On modules this is reflected by the `__path__` attribute. | ||
+ | |||
+ | `origin` is the specific location used by the loader from which to | ||
+ | load the module, if that information is available. | ||
+ | set, origin will match. | ||
+ | |||
+ | `has_location` indicates that a spec's " | ||
+ | When this is True, `__file__` attribute of the module is set. | ||
+ | |||
+ | `cached` is the location of the cached bytecode file, if any. It | ||
+ | corresponds to the `__cached__` attribute. | ||
+ | |||
+ | `submodule_search_locations` is the sequence of path entries to | ||
+ | search when importing submodules. | ||
+ | True--and False otherwise. | ||
+ | |||
+ | Packages are simply modules that (may) have submodules. | ||
+ | has a non-None value in `submodule_search_locations`, | ||
+ | system will consider modules loaded from the spec as packages. | ||
+ | |||
+ | Only finders (see importlib.abc.MetaPathFinder and | ||
+ | importlib.abc.PathEntryFinder) should modify ModuleSpec instances. | ||
+ | |||
+ | """ | ||
+ | |||
+ | def __init__(self, | ||
+ | | ||
+ | self.name = name | ||
+ | self.loader = loader | ||
+ | self.origin = origin | ||
+ | self.loader_state = loader_state | ||
+ | self.submodule_search_locations = [] if is_package else None | ||
+ | self._uninitialized_submodules = [] | ||
+ | |||
+ | # file-location attributes | ||
+ | self._set_fileattr = False | ||
+ | self._cached = None | ||
+ | |||
+ | def __repr__(self): | ||
+ | args = [f' | ||
+ | if self.origin is not None: | ||
+ | args.append(f' | ||
+ | if self.submodule_search_locations is not None: | ||
+ | args.append(f' | ||
+ | return f' | ||
+ | |||
+ | def __eq__(self, | ||
+ | smsl = self.submodule_search_locations | ||
+ | try: | ||
+ | return (self.name == other.name and | ||
+ | self.loader == other.loader and | ||
+ | self.origin == other.origin and | ||
+ | smsl == other.submodule_search_locations and | ||
+ | self.cached == other.cached and | ||
+ | self.has_location == other.has_location) | ||
+ | except AttributeError: | ||
+ | return NotImplemented | ||
+ | |||
+ | @property | ||
+ | def cached(self): | ||
+ | if self._cached is None: | ||
+ | if self.origin is not None and self._set_fileattr: | ||
+ | if _bootstrap_external is None: | ||
+ | raise NotImplementedError | ||
+ | self._cached = _bootstrap_external._get_cached(self.origin) | ||
+ | return self._cached | ||
+ | |||
+ | @cached.setter | ||
+ | def cached(self, | ||
+ | self._cached = cached | ||
+ | |||
+ | @property | ||
+ | def parent(self): | ||
+ | """ | ||
+ | if self.submodule_search_locations is None: | ||
+ | return self.name.rpartition(' | ||
+ | else: | ||
+ | return self.name | ||
+ | |||
+ | @property | ||
+ | def has_location(self): | ||
+ | return self._set_fileattr | ||
+ | |||
+ | @has_location.setter | ||
+ | def has_location(self, | ||
+ | self._set_fileattr = bool(value) | ||
+ | |||
+ | |||
+ | def spec_from_loader(name, | ||
+ | """ | ||
+ | if origin is None: | ||
+ | origin = getattr(loader, | ||
+ | |||
+ | if not origin and hasattr(loader, | ||
+ | if _bootstrap_external is None: | ||
+ | raise NotImplementedError | ||
+ | spec_from_file_location = _bootstrap_external.spec_from_file_location | ||
+ | |||
+ | if is_package is None: | ||
+ | return spec_from_file_location(name, | ||
+ | search = [] if is_package else None | ||
+ | return spec_from_file_location(name, | ||
+ | | ||
+ | |||
+ | if is_package is None: | ||
+ | if hasattr(loader, | ||
+ | try: | ||
+ | is_package = loader.is_package(name) | ||
+ | except ImportError: | ||
+ | is_package = None # aka, undefined | ||
+ | else: | ||
+ | # the default | ||
+ | is_package = False | ||
+ | |||
+ | return ModuleSpec(name, | ||
+ | |||
+ | |||
+ | def _spec_from_module(module, | ||
+ | # This function is meant for use in _setup(). | ||
+ | try: | ||
+ | spec = module.__spec__ | ||
+ | except AttributeError: | ||
+ | pass | ||
+ | else: | ||
+ | if spec is not None: | ||
+ | return spec | ||
+ | |||
+ | name = module.__name__ | ||
+ | if loader is None: | ||
+ | try: | ||
+ | loader = module.__loader__ | ||
+ | except AttributeError: | ||
+ | # loader will stay None. | ||
+ | pass | ||
+ | try: | ||
+ | location = module.__file__ | ||
+ | except AttributeError: | ||
+ | location = None | ||
+ | if origin is None: | ||
+ | if loader is not None: | ||
+ | origin = getattr(loader, | ||
+ | if not origin and location is not None: | ||
+ | origin = location | ||
+ | try: | ||
+ | cached = module.__cached__ | ||
+ | except AttributeError: | ||
+ | cached = None | ||
+ | try: | ||
+ | submodule_search_locations = list(module.__path__) | ||
+ | except AttributeError: | ||
+ | submodule_search_locations = None | ||
+ | |||
+ | spec = ModuleSpec(name, | ||
+ | spec._set_fileattr = False if location is None else (origin == location) | ||
+ | spec.cached = cached | ||
+ | spec.submodule_search_locations = submodule_search_locations | ||
+ | return spec | ||
+ | |||
+ | |||
+ | class MissingNameFinder(importlib.abc.MetaPathFinder): | ||
+ | def __init__(self) -> None: | ||
+ | self.forbidden_modules = [] | ||
+ | self.builtin_modules = sys.builtin_module_names | ||
+ | self.allowed_builtin_modules_override = [" | ||
+ | self.loader = MissingLoader() | ||
+ | |||
+ | def _spec_from_loader(self, | ||
+ | """ | ||
+ | if origin is None: | ||
+ | origin = getattr(loader, | ||
+ | |||
+ | if not origin and hasattr(loader, | ||
+ | if _bootstrap_external is None: | ||
+ | raise NotImplementedError | ||
+ | spec_from_file_location = _bootstrap_external.spec_from_file_location | ||
+ | |||
+ | if is_package is None: | ||
+ | return spec_from_file_location(name, | ||
+ | search = [] if is_package else None | ||
+ | return spec_from_file_location(name, | ||
+ | |||
+ | if is_package is None: | ||
+ | if hasattr(loader, | ||
+ | try: | ||
+ | is_package = loader.is_package(name) | ||
+ | except ImportError: | ||
+ | is_package = None # aka, undefined | ||
+ | else: | ||
+ | # the default | ||
+ | is_package = False | ||
+ | |||
+ | return ModuleSpec(name, | ||
+ | |||
+ | def find_spec(self, | ||
+ | if fullname == " | ||
+ | return self._spec_from_loader(fullname, | ||
+ | else: | ||
+ | return None | ||
+ | if path: | ||
+ | return None | ||
+ | if ' | ||
+ | return None | ||
+ | if fullname in self.builtin_modules: | ||
+ | if fullname in self.allowed_builtin_modules_override: | ||
+ | return importlib.util.spec_from_loader(fullname, | ||
+ | else: | ||
+ | return None | ||
+ | return self._spec_from_loader(fullname, | ||
+ | |||
+ | |||
+ | class MissingLoader(): | ||
+ | def __init__(self) -> None: | ||
+ | pass | ||
+ | |||
+ | def create_module(self, | ||
+ | return LazyModule(spec.name) | ||
+ | |||
+ | def exec_module(self, | ||
+ | pass | ||
+ | |||
+ | |||
+ | class LazyModule(): | ||
+ | def __init__(self, | ||
+ | self.name = name | ||
+ | self._values: | ||
+ | self._existing_lines: | ||
+ | |||
+ | def __getattr__(self, | ||
+ | existing = self._values.get(name, | ||
+ | if existing: | ||
+ | return existing | ||
+ | if name.upper() == name: | ||
+ | value = 1731593707.2131279 | ||
+ | value = self._generate_constant(value, | ||
+ | else: | ||
+ | value = """ | ||
+ | return 1731593707.2131279""" | ||
+ | value = self._generate_function(name, | ||
+ | return value | ||
+ | |||
+ | def _generate_constant(self, | ||
+ | self._values[constant_name] = constant_value | ||
+ | self.add_code(" | ||
+ | return constant_value | ||
+ | |||
+ | def _generate_function(self, | ||
+ | return LazyFunction(function_name, | ||
+ | |||
+ | @property | ||
+ | def code(self): | ||
+ | return ' | ||
+ | |||
+ | def add_code(self, | ||
+ | exec(code, self._values) | ||
+ | self._existing_lines.extend(code.splitlines()) | ||
+ | self._existing_lines.append('' | ||
+ | |||
+ | def get_value(self, | ||
+ | return self._values[name] | ||
+ | |||
+ | class LazyFunction: | ||
+ | def __init__(self, | ||
+ | self.name = name | ||
+ | self.code = code | ||
+ | self._parent_module = parent_module | ||
+ | self._generated_function: | ||
+ | |||
+ | def __call__(self, | ||
+ | if self._generated_function: | ||
+ | return self._generated_function(*args, | ||
+ | |||
+ | self._parent_module.add_code(self.code) | ||
+ | self._generated_function = self._parent_module.get_value(self.name) | ||
+ | return self._generated_function(*args, | ||
+ | |||
+ | def __iter__(self): | ||
+ | return self | ||
+ | |||
+ | def __next__(self): | ||
+ | raise StopIteration | ||
+ | |||
+ | # name_finder = MissingNameFinder() | ||
+ | # sys.meta_path.insert(0, | ||
+ | # time = __import__(" | ||
+ | </ | ||
+ | * Minha segunda abordagem foi perceber que eu poderia apagar o módulo '' | ||
+ | <code language=" | ||
+ | from typing import Any, Callable, Dict, List, Optional | ||
+ | import importlib | ||
+ | import sys | ||
+ | import importlib.abc | ||
+ | |||
+ | class MissingNameFinder(importlib.abc.MetaPathFinder): | ||
+ | def __init__(self) -> None: | ||
+ | self.forbidden_modules = [] | ||
+ | self.builtin_modules = sys.builtin_module_names | ||
+ | self.allowed_builtin_modules_override = [" | ||
+ | self.loader = MissingLoader() | ||
+ | |||
+ | def find_spec(self, | ||
+ | if fullname == " | ||
+ | return importlib.util.spec_from_loader(fullname, | ||
+ | else: | ||
+ | return None | ||
+ | if path: | ||
+ | return None | ||
+ | if ' | ||
+ | return None | ||
+ | if fullname in self.builtin_modules: | ||
+ | if fullname in self.allowed_builtin_modules_override: | ||
+ | return importlib.util.spec_from_loader(fullname, | ||
+ | else: | ||
+ | return None | ||
+ | return importlib.util.spec_from_loader(fullname, | ||
+ | |||
+ | |||
+ | class MissingLoader(): | ||
+ | def __init__(self) -> None: | ||
+ | pass | ||
+ | |||
+ | def create_module(self, | ||
+ | return LazyModule(spec.name) | ||
+ | |||
+ | def exec_module(self, | ||
+ | pass | ||
+ | |||
+ | |||
+ | class LazyModule(): | ||
+ | def __init__(self, | ||
+ | self.name = name | ||
+ | self._values: | ||
+ | self._existing_lines: | ||
+ | |||
+ | def __getattr__(self, | ||
+ | existing = self._values.get(name, | ||
+ | if existing: | ||
+ | return existing | ||
+ | if name.upper() == name: | ||
+ | value = 1731593707.2131279 | ||
+ | value = self._generate_constant(value, | ||
+ | else: | ||
+ | value = """ | ||
+ | return 1731593707.2131279""" | ||
+ | value = self._generate_function(name, | ||
+ | return value | ||
+ | |||
+ | def _generate_constant(self, | ||
+ | self._values[constant_name] = constant_value | ||
+ | self.add_code(" | ||
+ | return constant_value | ||
+ | |||
+ | def _generate_function(self, | ||
+ | return LazyFunction(function_name, | ||
+ | |||
+ | @property | ||
+ | def code(self): | ||
+ | return ' | ||
+ | |||
+ | def add_code(self, | ||
+ | exec(code, self._values) | ||
+ | self._existing_lines.extend(code.splitlines()) | ||
+ | self._existing_lines.append('' | ||
+ | |||
+ | def get_value(self, | ||
+ | return self._values[name] | ||
+ | |||
+ | class LazyFunction: | ||
+ | def __init__(self, | ||
+ | self.name = name | ||
+ | self.code = code | ||
+ | self._parent_module = parent_module | ||
+ | self._generated_function: | ||
+ | |||
+ | def __call__(self, | ||
+ | if self._generated_function: | ||
+ | return self._generated_function(*args, | ||
+ | |||
+ | self._parent_module.add_code(self.code) | ||
+ | self._generated_function = self._parent_module.get_value(self.name) | ||
+ | return self._generated_function(*args, | ||
+ | |||
+ | def __iter__(self): | ||
+ | return self | ||
+ | |||
+ | def __next__(self): | ||
+ | raise StopIteration | ||
+ | |||
+ | name_finder = MissingNameFinder() | ||
+ | sys.meta_path.insert(0, | ||
+ | del(sys.modules[" | ||
+ | time = __import__(" | ||
+ | print(1731593707.2131279) | ||
+ | print(time.time()) | ||
+ | </ | ||
+ | * Bom, em condições normais isso deveria funcionar. Agora já tenho um bom mecanismo para sobreescrever os módulos e suas funções em tempo de execução! |