使用importlib,是否有可能修改传入的源代码?

编程语言 2026-07-09

如下使用 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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章