PEP302需要实施细节

时间:2017-03-08 20:29:32

标签: python python-import

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

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

这很好用,但我有一个我无法弄清楚的具体情况。假设3个文件,main,foo和bar。 main设置导入钩然后导入foo和bar。 foo本身进口吧。所以情况是:

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__)

感谢。

1 个答案:

答案 0 :(得分:0)

在看了很多例子和文档之后,我得到了部分答案。

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

在我没有进行模块设置的导入运行时,我看到系统将foo.__package__ = 'foo'设置为None。但是在上面的代码中设置module.__package__不起作用。有些东西把它重置为foo。

有效的解决方案是设置module.__package__ = None(空字符串)。因此,添加模块的代码的工作是:

module.__package__ = ''

现在正在运行,模块foo和bar只导入一次。加密和未加密模块的行为看起来很相似。

如果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__) 未明确设置为module.__package__,我仍然无法理解''的设置位置。