【问题标题】:PEP302 implemention details needed需要 PEP 302 实施细节
【发布时间】:2017-03-08 20:29:32
【问题描述】:

我正在尝试使用基于 PEP302 的导入挂钩来捕获模块的导入,这样我就可以拥有一些在运行时加载的加密 .py 文件。我在https://github.com/citrusbyte/python-obfuscation 关注python 混淆模板。

基本思想很简单:使用插入到 sys.meta_path 中的 Finder() 函数来拦截导入命令,该函数捕获导入指令。 Finder 检查该模块是否是我们想要自己处理的模块,如果是,则返回一个自定义 Loader 对象。否则它会忽略导入。自定义加载器在 sys.modules 中创建一个条目,并读取 python 模块源并使用 PEP302 文档中定义的 exec 将其添加到新创建的模块中。

这几乎可以正常工作,但我有一种我无法弄清楚的具体情况。假设有 3 个文件,main、foo 和 bar。 main 设置导入钩子,然后导入 foo 和 bar。 foo 本身导入 bar。所以情况是:

main:
  set_import_hook
  import foo
  import bar
foo:
  import bar
bar:
  <irrelevant>

我在 Finder 函数中设置了调试语句作为钩子,以查看传递的内容。

当我有未加密的代码(即我自己不处理并添加到 sys.modules 的代码时,打印输出会显示以下行为:

Finder (foo)
Finder (bar) called from inside foo when foo itself is loaded
Finder (bar) called from main after returning from the import foo

当我自己处理和加载 foo 和 bar 文件时,行为如下:

Finder (foo)
Finder (foo.bar) tries to load bar in the context of foo
Finder (bar) called from main after returning from import foo

这会导致 sys.modules 中存在两个版本的 bar。如果您在这两种情况下查看 sys.modules.keys(),在第一种情况下它只显示 foo 和 bar。在第二种情况下,它显示 foo、foo.bar 和 bar。

我不明白这种行为。创建模块的过程如 PEP 302 文档中所述。这是我使用的:

    module = sys.modules.setdefault(name, imp.new_module(name))
    module.__file__ = filename
    module.__path__ = [os.path.dirname(os.path.abspath(file.name))]
    module.__loader__ = self
    sys.modules[name] = module
    exec(src, module.__dict__)

谢谢。

【问题讨论】:

    标签: python python-import


    【解决方案1】:

    在看了一堆例子和文档之后,我有了部分答案。

    在上面的代码中,我注意到我没有设置module.__package__。在导入过程中的某处,导致在模块定义中设置了 foo.__package__ = 'foo' 条目。这导致 foo 被视为一个包,并且它所做的任何导入都被视为相对于包目录的导入。

    在我没有进行模块设置的情况下针对导入运行时,我看到module.__package__ 被系统设置为无。但是在上面的代码中设置module.__package__ = None 不起作用。某些东西将其重置为 foo。

    有效的解决方案是设置module.__package__ = ''(空字符串)。所以添加模块的代码的工作部分是:

    module = sys.modules.setdefault(name, imp.new_module(name))
    module.__file__ = filename
    module.__path__ = [os.path.dirname(os.path.abspath(file.name))]
    module.__loader__ = self
    module.__package__ = ''
    sys.modules[name] = module
    exec(src, module.__dict__)
    

    现在可以工作了,模块 foo 和 bar 只被导入一次。加密和非加密模块的行为看起来相似。

    如果没有明确设置为'',我仍然不明白module.__package__ 的设置位置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-24
      • 1970-01-01
      • 2013-05-13
      相关资源
      最近更新 更多