Coverage for aipyapp/exec/python/mod_dict.py: 28%
64 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:02 +0200
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:02 +0200
1import sys
2import importlib.abc
3import importlib.util
5from loguru import logger
7class DictModuleLoader(importlib.abc.Loader):
8 def __init__(self, fullname, source):
9 self.fullname = fullname
10 self.source = source
12 def create_module(self, spec):
13 return None
15 def exec_module(self, module):
16 code_obj = self.source
17 if isinstance(self.source, str):
18 code_obj = compile(self.source, f"<{self.fullname}>", "exec")
19 exec(code_obj, module.__dict__)
21class DictModuleFinder(importlib.abc.MetaPathFinder):
22 def __init__(self, package, source_map):
23 self.package = package
24 self.source_map = source_map
26 def find_spec(self, fullname, path, target=None):
27 if fullname == self.package:
28 # 返回一个虚拟包的 spec,必须带 submodule_search_locations 说明这是包
29 spec = importlib.util.spec_from_loader(fullname, loader=None)
30 spec.submodule_search_locations = []
31 return spec
33 if not fullname.startswith(self.package + "."):
34 return None
35 subname = fullname[len(self.package) + 1:]
36 if subname in self.source_map:
37 loader = DictModuleLoader(fullname, self.source_map[subname])
38 return importlib.util.spec_from_loader(fullname, loader)
39 return None
42class DictModuleImporter:
43 def __init__(self, package="blocks"):
44 self.package = package
45 self.source_map = {}
46 self.finder = DictModuleFinder(self.package, self.source_map)
47 self.log = logger.bind(src='BlockImporter')
49 def add_module(self, name, code):
50 self.log.info('Add module', name=name)
51 self.source_map[name] = code
52 fullname = f"{self.package}.{name}"
53 if fullname in sys.modules:
54 del sys.modules[fullname]
56 def reload(self, fullname):
57 import importlib
58 if fullname in sys.modules:
59 return importlib.reload(sys.modules[fullname])
60 __import__(fullname)
61 return sys.modules[fullname]
63 def __enter__(self):
64 self._old_meta_path = sys.meta_path[:]
65 sys.meta_path.insert(0, self.finder)
66 return self
68 def __exit__(self, exc_type, exc_value, traceback):
69 sys.meta_path = self._old_meta_path
71# ========== 测试 ==========
73if __name__ == "__main__":
74 importer = DictModuleImporter("pkg")
75 importer.add_module("mod1", "def hello(): return 'hello from mod1'")
77 code = '''
78from pkg import mod1
79print(mod1.hello())
80'''
82 with importer:
83 exec(code)
85 # 修改模块代码,重新加载
86 importer.add_module("mod1", "def hello(): return 'updated mod1'")
88 with importer:
89 exec(code)