Coverage for aipyapp/exec/python/mod_dict.py: 28%

1 import sys

2 import importlib.abc

3 import importlib.util

4

5 from loguru import logger

6

7 class 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

21 class 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

42 class 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

73 if __name__ == "__main__":

74 importer = DictModuleImporter("pkg")

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

76

77 code = '''

78 from pkg import mod1

79 print(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)