【问题标题】:Package-specific import hooks in PythonPython 中特定于包的导入钩子
【发布时间】:2011-09-01 09:48:04
【问题描述】:

我正在创建一个 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 中做这种事情的正确方法吗?

【问题讨论】:

  • 我确定您的代码只是一个示例,但只是为了检查一下——如果您真的希望将 CLR 库移植到 Python,您应该使用 IronPython。 =)
  • @katrielalex:确实,我非常了解 IronPython。应该注意的是,它并没有完全解决您提到的问题 - 即,它要求您将 您的 Python 代码 移植到 CLR,而不是使用 CPython 中的 CLR 库(例如,与一些 C 库一起使用) )。但是,是的,在这种情况下,CLR 仅用作具体的、易于演示的示例;真正的东西是别的东西。

标签: python import-hooks


【解决方案1】:

总的来说,我认为您的方法看起来不错。我不会担心它是“全局的”,因为重点是指定您应该处理哪些路径。在导入逻辑中移动这个测试只会不必要地复杂化,所以它留给钩子的实现者来决定。

只是一个小问题,也许你可以使用sys.path_hooks?它似乎没有sys.meta_path“强大”一点@

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

【讨论】:

  • 我查看了__path__sys.path,它似乎是用于查找模块的文件 路径列表。现在,从技术上讲,我可以将我的包的__path__ 设置为我想要的任何东西,只要我还提供一个路径挂钩来处理它。但它闻起来像黑客(如果有人检查__path__ 怎么办?他们被允许这样做),而且似乎这并没有比使用meta_path 给我带来任何好处,因为过滤器仍然是全局的。另一方面,我现在必须确保我的所有包的 __path__ 得到妥善维护,以确保导入通过我的查找器
  • @Pavel:你的意思是path_hooks
  • 是的,评论提到了path_hooks。通过阅读文档,似乎向它注册的处理程序将提供sys.path(用于顶级包)或父包的__path__(用于子包)中的任何内容,所以我需要用我的动态投影模块的假值。我想我可以为clr 模块提供一个真实的路径,然后假装它是一个包含投影模块子目录的目录。但我仍然不确定与其他选择相比,这会给我带来什么。
  • @Pavel:我也不确定 :-) 对导入钩子不够熟悉,但根据我对他们的文档和 PEP 的理解,你在这里所做的似乎是一个很好的用法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
  • 1970-01-01
相关资源
最近更新 更多