Python中特定于包的导入挂钩

时间:2011-09-01 09:48:05

标签: python import-hooks

我正在创建一个Python模块,将不同语言/框架提供的API映射到Python中。理想情况下,我希望将其作为单个根包呈现,该包公开辅助方法,并将该其他框架中的所有命名空间映射到Python包/模块。为方便起见,我们以CLR为例:

import clr.System.Data
import clr.System.Windows.Forms

这里clr是一个神奇的顶级包,它暴露了CLR名称空间System.DataSystem.Windows.Forms子包/子模块(据我所知,包只是一个带子的模块模块/包;其他类型的成员仍然有效。)

我已阅读PEP-302并编写了一个简单的原型程序,通过安装自定义meta_path挂钩实现了类似的效果。 clr模块本身是一个合适的Python模块,在导入时,设置__path__ = [](使其成为一个包,以便import甚至尝试查找子模块,并注册钩子。钩子本身拦截任何包装的负载,其中包的全名以"clr."开头,使用imp.new_module()动态创建新模块,在sys.modules中注册,并使用小精灵灰尘和彩虹填充它使用原始API中的类和方法。这是代码:

clr.py

import sys
import imp

class MyLoader:
    def load_module(self, fullname):
        try:
            return sys.modules[fullname]
        except KeyError:
            pass
        print("--- load ---")
        print(fullname)
        m = imp.new_module(fullname)
        m.__file__ = "clr:" + fullname
        m.__path__ = []
        m.__loader__ = self
        m.speak = lambda: print("I'm " + fullname)
        sys.modules.setdefault(fullname, m)
        return m

class MyFinder:
    def find_module(self, fullname, path = None):
        print("--- find ---")
        print(fullname)
        print(path)
        if fullname.startswith("clr."):
            return MyLoader()            
        return None

print("--- init ---")
__path__ = []
sys.meta_path.append(MyFinder())

test.py

import clr.Foo.Bar.Baz

clr.Foo.speak()
clr.Foo.Bar.speak()
clr.Foo.Bar.Baz.speak()

总而言之,这似乎工作得很好。 Python保证链中的模块从左向右导入,因此始终首先导入clr,并设置钩子以允许导入链的其余部分。

但是,我想知道我在这里做的事情是否有点过分。毕竟,我是安装一个全局挂钩,即使我过滤掉了我不关心的那些,也会调用任何模块导入。是否有某种方法来安装一个钩子,只能从我的特定包中调用,而不是其他的?或者上面是在Python中做这种事情的正确方法吗?

1 个答案:

答案 0 :(得分:5)

总的来说,我认为你的方法看起来很好。我不担心它是“全局的”,因为重点是指定你应该处理哪些路径。在导入逻辑中移动此测试只会使其不必要地复杂化,因此需要由钩子的实现者来决定。

只是一个小问题,也许你可以使用sys.path_hooks?它似乎比sys.meta_path

更“强大”
  

sys.path_hooks是可调用的列表,将被签入   序列,以确定它们是否可以处理给定的路径项。该   使用一个参数(路径项)调用callable。可赎回的   如果无法处理路径项,则必须引发ImportError   如果它可以处理路径项,则返回导入器对象。