如何在保留调试的同时从字符串加载 Python 模块?

How do I load a Python module from a string while preserving debug?

我有兴趣加载一个 Python 模块,该模块的源代码嵌入到 C 扩展中。应该可以用 Python 的 importlib 机器做一些事情,比如 importlib.util.spec_from_file_location 这样当你调试时源代码就会出现。我将如何实施 importlib.util.spec_from_string?

作为快速修复,您可以将其转储到临时模块中,使用 exec 导入它,完成后删除临时模块。

这是一个玩具示例:

dummy_src =""" 
print("imported!") 
x = 5 
""" 

with open("temp.py", "w") as f: 
    f.write(dummy_src) 

exec("import temp") 
print(temp.x)

输出:

imported!
5

下面介绍了如何定义从字符串中获取模块源的加载器,然后创建模块并将其加载到 sys.modules 中。如果模块的源不在文件中,它可能很有用。如果已有文件,则使用 https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly

虽然 inspect.getsource(module) 适用于 importlib.abc.InspectLoader 的子类,只需要定义 get_source,回溯和 pdb 似乎不愿意显示源代码,直到您从 SourceLoader.

继承
import sys
import importlib.abc, importlib.util

class StringLoader(importlib.abc.SourceLoader):
    def __init__(self, data):
        self.data = data

    def get_source(self, fullname):
        return self.data

    def get_source(self, fullname):
        return self.data
    
    def get_data(self, path):
        return self.data.encode("utf-8")
    
    def get_filename(self, fullname):
        return "<not a real path>/" + fullname + ".py"
    
module_name = "testmodule"
with open("testmodule.py", "r") as module:
    loader = StringLoader(module.read())

spec = importlib.util.spec_from_loader(module_name, loader, origin="built-in")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)