使用importlib,是否有可能修改传入的源代码?
如下使用 importlib 时,是否有办法在创建模块和执行(以及编译)之间修改源代码?
import importlib.util
spec = importlib.util.spec_from_file_location(module_name, source_path)
module = importlib.util.module_from_spec(spec)
# <- source code needs to modified here
spec.loader.exec_module(module)
感谢 databaseTROY 对上述代码的贡献,出处见 Compile only if .pyc is out-of-date?
我意识到存在一种使用 CustomSourceLoader 类的方法,但这不幸地不能把已编译的代码保存为一个 .pyc。其思路是仅修改代码,并在原始源代码改变时再编译一个新的 .pyc。
解决方案
Not directly between module_from_spec() and exec_module().
module_from_spec() 仅仅创建模块对象。加载器在 exec_module() 运行时仍然负责读取、编译和缓存源代码,因此修改模块本身不会影响生成的 .pyc。
如果你想在保持正常的 .pyc 处理的同时修补源代码,通常的做法是对子类化 SourceFileLoader 并在编译前拦截源代码。
例如:
import importlib.machinery
class PatchedLoader(importlib.machinery.SourceFileLoader):
def source_to_code(self, data, path, *, _optimize=-1):
source = data.decode("utf-8")
# modify source here
source = source.replace("OLD", "NEW")
return compile(
source, path, "exec",
# dont_inherit=True stops the module that is being imported from
# inheriting compile options and future options that are being used
# for this file (eg. from __future__ import annotations). This is
# what SourceFileLoader uses to compile code
dont_inherit=True, optimize=_optimize
)
最后,为了能够使用加载器,对 spec_from_file_location 的调用变成:
spec = importlib.util.spec_from_file_location(
module_name,
source_path,
loader=PatchedLoader,
)
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。