Python package for creating and importing virtual modules.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

91 lines
2.7 KiB

import importlib
from importlib.abc import Loader, MetaPathFinder
import sys
__all__ = [
'MetaVirtualModule',
'VirtualModule',
'add_to_module',
'create_module',
]
# Our virtual module registry
registry = dict()
# Built in module class
module_cls = type(sys)
# Built in module spec class
spec_cls = type(sys.__spec__)
def create_module(module_name):
"""Function for create a new empty virtual module and register it"""
module = module_cls(module_name)
setattr(module, '__spec__', spec_cls(name=module_name, loader=VirtualModuleLoader))
registry[module_name] = module
return module
def add_to_module(module, name=None):
"""Decorator to register a function or class to a module"""
def wrapper(value):
key = name or getattr(value, '__name__', None)
if key:
setattr(module, key, value)
return value
return wrapper
class VirtualModuleLoader(Loader):
"""Module loader class used for pulling virtual modules from our registry"""
def create_module(spec):
if spec.name not in registry:
return None
return registry[spec.name]
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):
"""Module finder to register with sys.meta_path for finding module specs from our registry"""
def find_spec(fullname, path, target=None):
if fullname in registry:
return registry[fullname].__spec__
return None
class MetaVirtualModule(type):
"""Metaclass used for automatically creating and registering VirtualModule class definitions"""
def __init__(cls, name, bases, attrs):
# Initialize the class
super(MetaVirtualModule, cls).__init__(name, bases, attrs)
# Do not register our base class
if name == 'VirtualModule':
return
module_name = getattr(cls, '__module_name__', cls.__name__) or name
# DEV: `create_module` will registry this module for us
module = create_module(module_name)
# Copy over class attributes
for key, value in attrs.items():
if key in ('__name__', '__module_name__', '__module__', '__qualname__'):
continue
setattr(module, key, value)
class VirtualModule(metaclass=MetaVirtualModule):
"""Base virtual module class for creating modules from class definitions"""
pass
# Push our virtual module finder at the beginning of the sys.meta_path
# DEV: Push in first so we always look for virtual modules first
sys.meta_path.insert(0, VirtualModuleFinder)