From 0c1c3fae1041e5cbae88aa8545820e2f22755764 Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Sat, 25 Aug 2018 12:17:50 -0400 Subject: [PATCH] initial prototype --- virtualmod.py | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 virtualmod.py diff --git a/virtualmod.py b/virtualmod.py new file mode 100644 index 0000000..19b1619 --- /dev/null +++ b/virtualmod.py @@ -0,0 +1,96 @@ +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)