Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
work:2024-49 [2024/12/08 19:20] – created magsilvawork:2024-49 [2024/12/14 01:49] (current) magsilva
Line 6: Line 6:
     * Criação de conta no https://repost.aws. O re:Post é um fórum para perguntas sobre o AWS.     * Criação de conta no https://repost.aws. O re:Post é um fórum para perguntas sobre o AWS.
  
 +===== Pesquisa =====
 +  * Correção automática de programas
 +    * Um problema que tive recentemente foi quanto a redefinição de módulos (e suas funções) da biblioteca básica do Python. As funções que estão no módulo ''builtins'' é simples redefinir (basta definir a função manualmente, no escopo global), mas um módulo é importado durante a execução do programa (e qualquer definição no escopo global anterior seria sobreescrita).
 +    * Normalmente, poderíamos usar dublês (mocks) para sobrepor o comportamento de alguma função ou método de uma classe. No entanto, isso não é tecnicamente possível (ao menos com as ferramentas atuais) para os módulos e funções da biblioteca básica/padrão do Python (builtin).
 +    * Mais especificamente, eu precisava sobreescrever o comportamento da função ''time'' do módulo ''time''
 +    * Existe mais um adendo de que, por ser para uma disciplina introdutório, eu não queria criar um objeto ou forçar uma abstração além daquilo que os estudantes, que estão aprendendo o básico da programação, precisam.
 +    * Em tese, seria suficiente criar um arquivo '''time.py''' com a função em questão. Isso seria verdade se, no ambiente de avaliação automática que utilizo, o CodeRunner do Moodle, isso fosse permitido.
 +    * 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://github.com/python/cpython. Outro aspecto positivo é que, embora a implementação padrão do ''import'' seja em C (Python/import.c, em https://github.com/python/cpython), o carregamento de módulos também pode ser  realizado por código escrito em Python. O módulo ''importlib'' possui tudo que é necessário: https://github.com/python/cpython/blob/main/Lib/importlib/. Não é um módulo bonito e fácil de ler, mas servirá como inspiração para uma solução.
 +    * Um desenvolvedor criou um módulo [[https://pypi.org/project/module-found | ''module-found'']] que, usando o ''importlib'', faz algo bem legal: ele utiliza uma aplicação de modelo de linguagem (LLM), mais precisamente um modelo da OpenAI, para gerar automaticamente o código em Python, considerando uma descrição informada como parâmetro, referente a uma função de um módulo que não existem em Python. Ou seja, ao invés de lançar uma exceção de que o módulo ou função não foi encontrado, ele cria automaticamente o módulo e a função necessários. No caso, eu não preciso gerar automaticamente código, dado que eu sei exatamente o que eu preciso. No entanto, o mecanismo é o mesmo (com a diferença que eu quero sobreescrever um módulo ou função existente).
 +      * Só para complementar, outro módulo Python que faz algo nessa linha, no caso instalando automaticamente (em tempo de execução) os módulos não encontrados é o [[https://pypi.org/project/pipimport | ''pipimport'']].
 +    * 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, FrozenImporter e PathFinder. As duas primeiras são para carregar os módulos padrões do Python e a terceira é para carregar os módulos disponíveis no sistema de arquivo (este último utiliza a variável ''sys.path'', amplamente empregada para configuração de ambientes virtuais).
 +      * As classes a serem utilizadas estão definidas na variável ''sys.meta_path''. Basicamente, ao procurar por um nome (módulo, função ou variável), o Python percorre os objetos disponíveis nessa variável, utilizando-os para encontrar o que deseja.
 +    * 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 ''time'' não pode. No código de importação do módulo ''importlib'' é feito um grande esforço para não permitir a sobreescrita de módulos builtins. Na função ''_find_and_load()'' do ''_bootstrap.py'', antes de começar a busca pelo módulo é verificado se ele está presente em ''sys.modules'' (um dicionário de nome de módulo para o módulo em si). Por exemplo, hoje, em minha máquina, tenho os seguintes módulos nessa variável (e olha o ''time'' lá no fim!): _abc, abc, abrt_exception_handler3, _ast, ast, atexit, builtins, _codecs, codecs, _collections, collections, _collections_abc, collections.abc, contextlib, copyreg, _datetime, datetime, dis, _distutils_hack, encodings, encodings.aliases, encodings.utf_8, encodings.utf_8_sig, enum, errno, _frozen_importlib, _frozen_importlib_external, _functools, functools, __future__, genericpath, google, _imp, importlib, importlib._abc, importlib._bootstrap, importlib._bootstrap_external, importlib.machinery, importlib.util, inspect, _io, io, itertools, keyword, linecache, logging, __main__, marshal, _opcode, opcode, _operator, operator, os, os.path, paste, platform, posix, posixpath, re, readline, re._casefix, re._compiler, re._constants, re._parser, reprlib, rlcompleter, _signal, site, _sitebuiltins, _sre, _stat, stat, _string, string, sys, syslog, systemd, systemd.id128, systemd._journal, systemd.journal, systemd._reader, textwrap, _thread, threading, **time**, token, _tokenize, tokenize, traceback, types, _uuid, uuid, _warnings, warnings, _weakref, weakref, _weakrefset, zipimport.
 +    * 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 ''time.time()''. O código compreende a criação de duas classes, ''MissingNameFinder'' e ''MissingLoader'', responsáveis por procurar o nome e, ao não encontrá-lo, usar o ''MissingLoader'' para criar, em tempo de execução, o módulo (classe ''LazyModule''), constante ou função (classe ''LazyFunction''). O que sobrou é código aproveitado do ''importlib'':
 +<code language="python">
 +from typing import Any, Callable, Dict, List, Optional
 +import importlib
 +import sys
 +import importlib.abc
 +import _imp
 +
 +def _verbose_message(message, *args, verbosity=1):
 +    """Print the message to stderr if -v/PYTHONVERBOSE is turned on."""
 +    if sys.flags.verbose >= verbosity:
 +        if not message.startswith(('#', 'import ')):
 +            message = '# ' + message
 +        print(message.format(*args), file=sys.stderr)
 +
 +
 +def _find_spec(name, path, target=None):
 +    """Find a module's spec."""
 +    meta_path = sys.meta_path
 +    if meta_path is None:
 +        # PyImport_Cleanup() is running or has been called.
 +        raise ImportError("sys.meta_path is None, Python is likely shutting down")
 +
 +    if not meta_path:
 +        _warnings.warn('sys.meta_path is empty', ImportWarning)
 +
 +    # 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:
 +           continue
 +        else:
 +            spec = find_spec(name, path, target)
 +        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't
 +                    # 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, package, level):
 +    """Verify arguments are "sane"."""
 +    if not isinstance(name, str):
 +        raise TypeError(f'module name must be str, not {type(name)}')
 +    if level < 0:
 +        raise ValueError('level must be >= 0')
 +    if level > 0:
 +        if not isinstance(package, str):
 +            raise TypeError('__package__ not set to a string')
 +        elif not package:
 +            raise ImportError('attempted relative import with no known parent package')
 +    if not name and level == 0:
 +        raise ValueError('Empty module name')
 +
 +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')
 +    base = bits[0]
 +    return f'{base}.{name}' if name else base
 +
 +def _init_module_attrs(spec, module, *, override=False):
 +    # 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, '__name__', None) is None):
 +        try:
 +            module.__name__ = spec.name
 +        except AttributeError:
 +            pass
 +    # __loader__
 +    if override or getattr(module, '__loader__', None) is None:
 +        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.  Namespace packages have no location, but their
 +                # __spec__.origin is None, and thus their module.__file__
 +                # should also be None for consistency.  While a bit of a hack,
 +                # this is the best place to ensure this consistency.
 +                #
 +                # See # https://docs.python.org/3/library/importlib.html#importlib.abc.Loader.load_module
 +                # and bpo-32305
 +                module.__file__ = None
 +        try:
 +            module.__loader__ = loader
 +        except AttributeError:
 +            pass
 +    # __package__
 +    if override or getattr(module, '__package__', None) is None:
 +        try:
 +            module.__package__ = spec.parent
 +        except AttributeError:
 +            pass
 +    # __spec__
 +    try:
 +        module.__spec__ = spec
 +    except AttributeError:
 +        pass
 +    # __path__
 +    if override or getattr(module, '__path__', None) is None:
 +        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__/__cached__
 +    if spec.has_location:
 +        if override or getattr(module, '__file__', None) is None:
 +            try:
 +                module.__file__ = spec.origin
 +            except AttributeError:
 +                pass
 +
 +        if override or getattr(module, '__cached__', None) is None:
 +            if spec.cached is not None:
 +                try:
 +                    module.__cached__ = spec.cached
 +                except AttributeError:
 +                    pass
 +    return module
 +
 +def module_from_spec(spec):
 +    """Create a module based on the provided spec."""
 +    # Typically loaders will not implement create_module().
 +    module = None
 +    if hasattr(spec.loader, 'create_module'):
 +        # If create_module() returns `None` then it means default
 +        # module creation should be used.
 +        module = spec.loader.create_module(spec)
 +    elif hasattr(spec.loader, 'exec_module'):
 +        raise ImportError('loaders that define exec_module() '
 +                          'must also define create_module()')
 +    if module is None:
 +        module = _new_module(spec.name)
 +    _init_module_attrs(spec, module)
 +    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, 'exec_module'):
 +            msg = (f"{_object_name(spec.loader)}.exec_module() not found; "
 +                    "falling back to load_module()")
 +            _warnings.warn(msg, ImportWarning)
 +            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
 +
 +    return module
 +
 +def _find_and_load_unlocked(name, import_):
 +    path = None
 +    parent = name.rpartition('.')[0]
 +    parent_spec = None
 +    if parent:
 +        if parent not in sys.modules:
 +            _call_with_frames_removed(import_, parent)
 +        # 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'{_ERR_MSG_PREFIX}{name!r}; {parent!r} is not a package'
 +            raise ModuleNotFoundError(msg, name=name) from None
 +        parent_spec = parent_module.__spec__
 +        child = name.rpartition('.')[2]
 +    spec = _find_spec(name, path)
 +    if spec is None:
 +        raise ModuleNotFoundError(f'{_ERR_MSG_PREFIX}{name!r}', name=name)
 +    else:
 +        if parent_spec:
 +            # Temporarily add child we are currently importing to parent's
 +            # _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, child, module)
 +        except AttributeError:
 +            msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
 +            _warnings.warn(msg, ImportWarning)
 +    return module
 +
 +
 +
 +
 +def _find_and_load(name, import_):
 +    """Find and load the module."""
 +
 +    # Optimization: we avoid unneeded module locking if the module
 +    # already exists in sys.modules and is fully initialized.
 +    # module = sys.modules.get(name, _NEEDS_LOADING)
 +    return _find_and_load_unlocked(name, import_)
 +
 +
 +    if module is None:
 +        message = f'import of {name} halted; None in sys.modules'
 +        raise ModuleNotFoundError(message, name=name)
 +
 +    return module
 +
 +
 +def _gcd_import(name, package=None, level=0):
 +    """Import and return the module based on its name, the package the call is
 +    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, package, level)
 +    if level > 0:
 +        name = _resolve_name(name, package, level)
 +    return _find_and_load(name, _gcd_import)
 +
 +
 +def __import__(name, globals=None, locals=None, fromlist=(), level=0):
 +    """Import a module.
 +
 +    The 'globals' argument is used to infer where the import is occurring from
 +    to handle relative imports. The 'locals' argument is ignored. The
 +    'fromlist' argument specifies what should exist as attributes on the module
 +    being imported (e.g. ``from module import <fromlist>``).  The 'level'
 +    argument represents the package location to import from in a relative
 +    import (e.g. ``from ..pkg import mod`` would have a 'level' of 2).
 +
 +    """
 +    if level == 0:
 +        module = _gcd_import(name)
 +    else:
 +        globals_ = globals if globals is not None else {}
 +        package = _calc___package__(globals_)
 +        module = _gcd_import(name, package, level)
 +    if not fromlist:
 +        # Return up to the first dot in 'name'. This is complicated by the fact
 +        # that 'name' may be relative.
 +        if level == 0:
 +            return _gcd_import(name.partition('.')[0])
 +        elif not name:
 +            return module
 +        else:
 +            # Figure out where to slice the module's name up to the first dot
 +            # in 'name'.
 +            cut_off = len(name) - len(name.partition('.')[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)
 +    else:
 +        return module
 +
 +
 +class ModuleSpec:
 +    """The specification for a module, used for loading.
 +
 +    A module's spec is the source for information about the module.  For
 +    data associated with the module, including source, use the spec's
 +    loader.
 +
 +    `name` is the absolute name of the module.  `loader` is the loader
 +    to use when loading the module.  `parent` is the name of the
 +    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.  When filename is
 +    set, origin will match.
 +
 +    `has_location` indicates that a spec's "origin" reflects a location.
 +    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.  If set, is_package should be
 +    True--and False otherwise.
 +
 +    Packages are simply modules that (may) have submodules.  If a spec
 +    has a non-None value in `submodule_search_locations`, the import
 +    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, name, loader, *, origin=None, loader_state=None,
 +                 is_package=None):
 +        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'name={self.name!r}', f'loader={self.loader!r}']
 +        if self.origin is not None:
 +            args.append(f'origin={self.origin!r}')
 +        if self.submodule_search_locations is not None:
 +            args.append(f'submodule_search_locations={self.submodule_search_locations}')
 +        return f'{self.__class__.__name__}({", ".join(args)})'
 +
 +    def __eq__(self, other):
 +        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, cached):
 +        self._cached = cached
 +
 +    @property
 +    def parent(self):
 +        """The name of the module's parent."""
 +        if self.submodule_search_locations is None:
 +            return self.name.rpartition('.')[0]
 +        else:
 +            return self.name
 +
 +    @property
 +    def has_location(self):
 +        return self._set_fileattr
 +
 +    @has_location.setter
 +    def has_location(self, value):
 +        self._set_fileattr = bool(value)
 +
 +
 +def spec_from_loader(name, loader, *, origin=None, is_package=None):
 +    """Return a module spec based on various loader methods."""
 +    if origin is None:
 +        origin = getattr(loader, '_ORIGIN', None)
 +
 +    if not origin and hasattr(loader, 'get_filename'):
 +        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, loader=loader)
 +        search = [] if is_package else None
 +        return spec_from_file_location(name, loader=loader,
 +                                       submodule_search_locations=search)
 +
 +    if is_package is None:
 +        if hasattr(loader, 'is_package'):
 +            try:
 +                is_package = loader.is_package(name)
 +            except ImportError:
 +                is_package = None  # aka, undefined
 +        else:
 +            # the default
 +            is_package = False
 +
 +    return ModuleSpec(name, loader, origin=origin, is_package=is_package)
 +
 +
 +def _spec_from_module(module, loader=None, origin=None):
 +    # 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, '_ORIGIN', None)
 +        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, loader, origin=origin)
 +    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 = ["time"]
 +        self.loader = MissingLoader()
 + 
 +    def _spec_from_loader(self, name, loader, *, origin=None, is_package=None):
 +        """Return a module spec based on various loader methods."""
 +        if origin is None:
 +            origin = getattr(loader, '_ORIGIN', None)
 +
 +        if not origin and hasattr(loader, 'get_filename'):
 +            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, loader=loader)
 +            search = [] if is_package else None
 +            return spec_from_file_location(name, loader=loader, submodule_search_locations=search)
 +
 +        if is_package is None:
 +            if hasattr(loader, 'is_package'):
 +                try:
 +                    is_package = loader.is_package(name)
 +                except ImportError:
 +                    is_package = None  # aka, undefined
 +            else:
 +                # the default
 +                is_package = False
 +
 +        return ModuleSpec(name, loader, origin=origin, is_package=is_package)
 + 
 +    def find_spec(self, fullname, path, target = None):
 +        if fullname == "time" or fullname == "time30":
 +            return self._spec_from_loader(fullname, self.loader)
 +        else:
 +            return None
 +        if path:
 +            return None
 +        if '.' in fullname or fullname in self.forbidden_modules:
 +            return None
 +        if fullname in self.builtin_modules:
 +            if fullname in self.allowed_builtin_modules_override:
 +                return importlib.util.spec_from_loader(fullname, self.loader)
 +            else:
 +                return None
 +        return self._spec_from_loader(fullname, self.loader)
 +
 +
 +class MissingLoader():
 +    def __init__(self) -> None:
 +        pass
 +
 +    def create_module(self, spec):
 +        return LazyModule(spec.name)
 +
 +    def exec_module(self, _module):
 +        pass
 +
 +
 +class LazyModule():
 +    def __init__(self, name: str) -> None:
 +        self.name = name
 +        self._values: Dict[str, Any] = {}
 +        self._existing_lines: List[str] = []
 +
 +    def __getattr__(self, name: str) -> Any:
 +        existing = self._values.get(name, None)
 +        if existing:
 +            return existing
 +        if name.upper() == name:
 +            value = 1731593707.2131279
 +            value = self._generate_constant(value, value)
 +        else:
 +            value = """def time():
 +    return 1731593707.2131279"""
 +            value = self._generate_function(name, value)
 +        return value
 +
 +    def _generate_constant(self, constant_name: str, constant_value: Any) -> Any:
 +        self._values[constant_name] = constant_value
 +        self.add_code("%s = %s" % (constante_name, constant_string))
 +        return constant_value
 +
 +    def _generate_function(self, function_name: str, function_code: str) -> Callable:
 +        return LazyFunction(function_name, function_code, self)
 +
 +    @property
 +    def code(self):
 +        return '\n'.join(self._existing_lines)
 +
 +    def add_code(self, code: str):
 +        exec(code, self._values)
 +        self._existing_lines.extend(code.splitlines())
 +        self._existing_lines.append('')
 +
 +    def get_value(self, name: str) -> Any:
 +        return self._values[name]
 +    
 +class LazyFunction:
 +    def __init__(self, name: str, code: str, parent_module: LazyModule) -> None:
 +        self.name = name
 +        self.code = code
 +        self._parent_module = parent_module
 +        self._generated_function: Optional[Callable] = None
 +
 +    def __call__(self, *args, **kwargs) -> Any:
 +        if self._generated_function:
 +            return self._generated_function(*args, **kwargs)
 +
 +        self._parent_module.add_code(self.code)
 +        self._generated_function = self._parent_module.get_value(self.name)
 +        return self._generated_function(*args, **kwargs)
 +
 +    def __iter__(self):
 +        return self
 +
 +    def __next__(self):
 +        raise StopIteration
 +
 +# name_finder = MissingNameFinder()
 +# sys.meta_path.insert(0, name_finder)
 +# time = __import__("time")
 +</code>
 +    * Minha segunda abordagem foi perceber que eu poderia apagar o módulo ''time'' da variável ''sys.modules''. Com isso eu tenho um código bem mais enxuto:
 +<code language="Python">
 +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 = ["time"]
 +        self.loader = MissingLoader()
 + 
 +    def find_spec(self, fullname, path, target = None):
 +        if fullname == "time" or fullname == "time30":
 +            return importlib.util.spec_from_loader(fullname, self.loader)
 +        else:
 +            return None
 +        if path:
 +            return None
 +        if '.' in fullname or fullname in self.forbidden_modules:
 +            return None
 +        if fullname in self.builtin_modules:
 +            if fullname in self.allowed_builtin_modules_override:
 +                return importlib.util.spec_from_loader(fullname, self.loader)
 +            else:
 +                return None
 +        return importlib.util.spec_from_loader(fullname, self.loader)
 +
 +
 +class MissingLoader():
 +    def __init__(self) -> None:
 +        pass
 +
 +    def create_module(self, spec):
 +        return LazyModule(spec.name)
 +
 +    def exec_module(self, _module):
 +        pass
 +
 +
 +class LazyModule():
 +    def __init__(self, name: str) -> None:
 +        self.name = name
 +        self._values: Dict[str, Any] = {}
 +        self._existing_lines: List[str] = []
 +
 +    def __getattr__(self, name: str) -> Any:
 +        existing = self._values.get(name, None)
 +        if existing:
 +            return existing
 +        if name.upper() == name:
 +            value = 1731593707.2131279
 +            value = self._generate_constant(value, value)
 +        else:
 +            value = """def time():
 +    return 1731593707.2131279"""
 +            value = self._generate_function(name, value)
 +        return value
 +
 +    def _generate_constant(self, constant_name: str, constant_value: Any) -> Any:
 +        self._values[constant_name] = constant_value
 +        self.add_code("%s = %s" % (constante_name, constant_string))
 +        return constant_value
 +
 +    def _generate_function(self, function_name: str, function_code: str) -> Callable:
 +        return LazyFunction(function_name, function_code, self)
 +
 +    @property
 +    def code(self):
 +        return '\n'.join(self._existing_lines)
 +
 +    def add_code(self, code: str):
 +        exec(code, self._values)
 +        self._existing_lines.extend(code.splitlines())
 +        self._existing_lines.append('')
 +
 +    def get_value(self, name: str) -> Any:
 +        return self._values[name]
 +    
 +class LazyFunction:
 +    def __init__(self, name: str, code: str, parent_module: LazyModule) -> None:
 +        self.name = name
 +        self.code = code
 +        self._parent_module = parent_module
 +        self._generated_function: Optional[Callable] = None
 +
 +    def __call__(self, *args, **kwargs) -> Any:
 +        if self._generated_function:
 +            return self._generated_function(*args, **kwargs)
 +
 +        self._parent_module.add_code(self.code)
 +        self._generated_function = self._parent_module.get_value(self.name)
 +        return self._generated_function(*args, **kwargs)
 +
 +    def __iter__(self):
 +        return self
 +
 +    def __next__(self):
 +        raise StopIteration
 +
 +name_finder = MissingNameFinder()
 +sys.meta_path.insert(0, name_finder)
 +del(sys.modules["time"])
 +time = __import__("time"!
 +print(1731593707.2131279)
 +print(time.time())
 +</code>
 +    * 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!