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

1import sys 

2import importlib.abc 

3import importlib.util 

4 

5from loguru import logger 

6 

7class DictModuleLoader(importlib.abc.Loader): 

8 def __init__(self, fullname, source): 

9 self.fullname = fullname 

10 self.source = source 

11 

12 def create_module(self, spec): 

13 return None 

14 

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__) 

20 

21class DictModuleFinder(importlib.abc.MetaPathFinder): 

22 def __init__(self, package, source_map): 

23 self.package = package 

24 self.source_map = source_map 

25 

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 

32 

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 

40 

41 

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') 

48 

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] 

55 

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] 

62 

63 def __enter__(self): 

64 self._old_meta_path = sys.meta_path[:] 

65 sys.meta_path.insert(0, self.finder) 

66 return self 

67 

68 def __exit__(self, exc_type, exc_value, traceback): 

69 sys.meta_path = self._old_meta_path 

70 

71# ========== 测试 ========== 

72 

73if __name__ == "__main__": 

74 importer = DictModuleImporter("pkg") 

75 importer.add_module("mod1", "def hello(): return 'hello from mod1'") 

76 

77 code = ''' 

78from pkg import mod1 

79print(mod1.hello()) 

80''' 

81 

82 with importer: 

83 exec(code) 

84 

85 # 修改模块代码,重新加载 

86 importer.add_module("mod1", "def hello(): return 'updated mod1'") 

87 

88 with importer: 

89 exec(code)