如何从Python代码对象截取代码片段执行以适配特殊导入场景?
问题:基于模块代码对象实现懒导入代码片段的截取与执行
这是此前关于「Python类定义或元类是否存在‘类定义完成后’的特殊方法?」问题的续篇。为处理循环引用,我实现了可保留类型提示的懒导入方案,代码如下:
import dataclasses import inspect lazy_chunks_key = '__lazy_chunks__' @dataclasses.dataclass class LazyChunk: _code: str _global: dict _local: dict def _load(self): if self._code: exec(self._code, self._global, self._local) self._code = '' def __getattr__(self, item): self._load() if item in self._local: return self._local[item] if item in self._global: return self._global[item] raise NameError(item) def __bool__(self): return False def lazy_chunk(): import re stack = inspect.stack()[1] with open(stack.filename, encoding='utf-8') as _src: src = iter(_src) for _ in range(stack.lineno): next(src) spacing, code = re.match(r"( *)([^ ].*)?", next(src)).groups() chunk_ind = len(spacing) code += '\n' while (line := next(src, None)) is not None: spacing, line_code = re.match(r"( *)([^ ].*)?", line).groups() if not line_code: continue if (_ind := len(spacing)) < chunk_ind: break code += ' ' * (_ind - chunk_ind) + line_code + '\n' frame = stack.frame frame.f_locals.setdefault(lazy_chunks_key, []).append(res := LazyChunk(code.strip(), frame.f_globals, frame.f_locals)) return res
在结构体工厂中,我通过lazy chunk获取相关类型:
def set_fields_from_annotations(cls: typing.Type[_CStruct]) -> typing.Type[_CStruct]: if not (annotations := getattr(cls, '__annotations__', None)): return cls global_namespace = getattr(sys.modules.get(cls.__module__, None), '__dict__', {}) global_namespace[cls.__name__] = cls local_namespace = dict(vars(cls)) def str_eval_with_lazy(v): if isinstance(v, str): for chunk in global_namespace.pop(lazy_chunks_key, ()): chunk._load() return eval(v, global_namespace, local_namespace) return v ... # passing `str_eval_with_lazy` to fields and call it to get actual type when it is needed
lazy_chunk的使用示例如下:
# tree.py from .node import Node @set_fields_from_annotations class Node(ctypes.Structure): first_node: Node # node.py if lazy_chunk(): from .tree import Tree @set_fields_from_annotations class Node(ctypes.Structure): tree: 'Tree' value: ctypes.c_uint key: ctypes.c_uint parent: 'ctypes.POINTER(Node)' child: 'ctypes.POINTER(Node)'
但扩展使用场景时,从源码文件切割代码的方式不再可行,例如zipimport或导入预编译代码的场景。为解决该问题,请问是否有方法获取模块代码对象,截取代码片段并在eval中运行?
内容的提问来源于stack exchange,提问作者nyao
相关产品推荐
相关产品推荐

