如何覆盖Python导入?

Eva*_*ice 32 python import preprocessor overriding

我正在研究pypreprocessor,这是一个采用c风格指令的预处理器,我已经能够使它像传统的预处理器一样工作(它是自耗的,并且即时执行后处理代码),除了它打破了库进口.

问题是:预处理器运行文件,处理它,输出到临时文件,exec()临时文件.导入的库需要处理稍有不同,因为它们不会被执行,而是被加载并且可以被调用者模块访问.

我需要做的是:中断导入(因为预处理器在导入过程中运行),将后处理代码作为tempModule加载,并用tempModule替换原始导入以欺骗调用脚本认为tempModule是原始模块的导入.

到目前为止,我到处搜索,没有解决方案.

这个Stack Overflow问题是我到目前为止提供答案的最接近的问题: 在Python中覆盖名称空间

这就是我所拥有的.

# Remove the bytecode file created by the first import
os.remove(moduleName + '.pyc')

# Remove the first import
del sys.modules[moduleName]

# Import the postprocessed module
tmpModule = __import__(tmpModuleName)

# Set first module's reference to point to the preprocessed module
sys.modules[moduleName] = tmpModule
Run Code Online (Sandbox Code Playgroud)

moduleName是原始模块的名称,tmpModuleName是后处理代码文件的名称.

奇怪的是这个解决方案仍然完全正常运行,好像第一个模块完成正常加载一样; 除非你删除最后一行,否则你会得到一个模块未找到错误.

希望Stack Overflow上有人比我更了解进口,因为这个让我难过.

注意:我只会授予一个解决方案,或者,如果在Python中无法做到这一点; 最好,最详细的解释为什么这不是不可能的.

更新:对于任何感兴趣的人,这是工作代码.

if imp.lock_held() is True:
    del sys.modules[moduleName]
    sys.modules[tmpModuleName] = __import__(tmpModuleName)
    sys.modules[moduleName] = __import__(tmpModuleName)
Run Code Online (Sandbox Code Playgroud)

'imp.lock_held'部分检测模块是否作为库加载.以下几行完成其余的工作.

小智 35

这回答了你的问题了吗?第二个导入就是诀窍.

Mod_1.py

def test_function():
    print "Test Function -- Mod 1"
Run Code Online (Sandbox Code Playgroud)

Mod_2.py

def test_function():
    print "Test Function -- Mod 2"
Run Code Online (Sandbox Code Playgroud)

Test.py

#!/usr/bin/python

import sys

import Mod_1

Mod_1.test_function()

del sys.modules['Mod_1']

sys.modules['Mod_1'] = __import__('Mod_2')

import Mod_1

Mod_1.test_function()
Run Code Online (Sandbox Code Playgroud)

  • @EvanPlaice重要的是你可以做第二个`import Mod_1`.这样做不会重新加载或刷新真实模块 - 它已被"Mod_2"永久取代. (3认同)
  • 非常感谢,这几乎与我的实现相同,但是它通过一个实际有用的工作示例帮助我正确了。注意:第二个'import Mod_1'是多余的,因为它前面的那一行已经处理了。 (2认同)

Anu*_*yal 12

要定义不同的导入行为或完全破坏导入过程,您需要编写导入钩子.见PEP 302.

例如,

import sys

class MyImporter(object):

    def find_module(self, module_name, package_path):
        # Return a loader
        return self

    def load_module(self, module_name):
        # Return a module
        return self

sys.meta_path.append(MyImporter())

import now_you_can_import_any_name
print now_you_can_import_any_name
Run Code Online (Sandbox Code Playgroud)

它输出:

<__main__.MyImporter object at 0x009F85F0>
Run Code Online (Sandbox Code Playgroud)

所以基本上它返回一个新模块(可以是任何对象),在这种情况下本身.您可以通过返回processe_xxx导入来使用它来改变导入行为xxx.

IMO:Python不需要预处理器.无论你正在完成什么都可以在Python本身完成,因为它非常动态,例如,以调试示例为例,在文件顶部有什么问题

debug = 1
Run Code Online (Sandbox Code Playgroud)

然后

if debug:
   print "wow"
Run Code Online (Sandbox Code Playgroud)