import importlib
|
|
from importlib.abc import Loader, MetaPathFinder
|
|
import sys
|
|
|
|
registry = dict()
|
|
|
|
module_cls = type(sys)
|
|
spec_cls = type(sys.__spec__)
|
|
|
|
|
|
class VirtualModule:
|
|
__slots__ = ['name', 'module', 'spec']
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.module = module_cls(name)
|
|
self.spec = spec_cls(name=name, loader=VirtualModuleLoader)
|
|
setattr(self.module, '__spec__', self.spec)
|
|
|
|
def set_attribute(self, name, value):
|
|
setattr(self.module, name, value)
|
|
|
|
def delete_attribute(self, name):
|
|
delattr(self.module, name)
|
|
|
|
|
|
def create_module(name):
|
|
module = VirtualModule(name)
|
|
registry[name] = module
|
|
return module
|
|
|
|
|
|
def copy_module(module):
|
|
name = module.__name__
|
|
if hasattr(module, '__spec__'):
|
|
name = module.__spec__.name
|
|
|
|
virt_mod = VirtualModule(name)
|
|
for key, value in module.__dict__.items():
|
|
if key in ('__spec__', '__name__', '__loader__', '__package__'):
|
|
continue
|
|
virt_mod.set_attribute(key, value)
|
|
|
|
registry[name] = virt_mod
|
|
importlib.reload(module)
|
|
return virt_mod
|
|
|
|
|
|
def as_module(cls_or_name):
|
|
if isinstance(cls_or_name, str):
|
|
cls = None
|
|
name = cls_or_name
|
|
elif isinstance(cls_or_name, type):
|
|
cls = cls_or_name
|
|
name = getattr(cls, '__module_name__', cls.__name__)
|
|
else:
|
|
raise ValueError('Expected as_module to be passed a string or a class type')
|
|
|
|
def wrapper(cls):
|
|
module = create_module(name)
|
|
|
|
for key, value in cls.__dict__.items():
|
|
if key.startswith('__') and key.endswith('__'):
|
|
continue
|
|
|
|
module.set_attribute(key, value)
|
|
return module
|
|
|
|
if cls is None:
|
|
return wrapper
|
|
return wrapper(cls)
|
|
|
|
|
|
class VirtualModuleLoader(Loader):
|
|
def create_module(spec):
|
|
if spec.name not in registry:
|
|
return None
|
|
|
|
return registry[spec.name].module
|
|
|
|
def exec_module(module):
|
|
module_name = module.__name__
|
|
if hasattr(module, '__spec__'):
|
|
module_name = module.__spec__.name
|
|
|
|
sys.modules[module_name] = module
|
|
|
|
|
|
class VirtualModuleFinder(MetaPathFinder):
|
|
def find_spec(fullname, path, target=None):
|
|
if fullname in registry:
|
|
return registry[fullname].spec
|
|
return None
|
|
|
|
|
|
sys.meta_path.insert(0, VirtualModuleFinder)
|