Python中特定于包的导入挂钩

Python中特定于包的导入挂钩,python,import-hooks,Python,Import Hooks,我正在创建一个Python模块,将不同语言/框架提供的API映射到Python中。理想情况下,我希望将其显示为一个根包,公开助手方法,并将其他框架中的所有名称空间映射到Python包/模块。为了方便起见,我们以CLR为例: import clr.System.Data import clr.System.Windows.Forms 这里的clr是一个神奇的顶级包,它公开了clr命名空间System.Data和System.Windows.Forms子包/子模块(据我所知,包只是一个包含子模块/

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

import clr.System.Data
import clr.System.Windows.Forms
这里的
clr
是一个神奇的顶级包,它公开了clr命名空间
System.Data
System.Windows.Forms
子包/子模块(据我所知,包只是一个包含子模块/包的模块;在其中包含其他类型的成员仍然有效)

我读过并写过一个简单的原型程序,它通过安装一个自定义的
meta_路径
hook实现了类似的效果。
clr
模块本身是一个合适的Python模块,它在导入时设置
\uuuu path\uuuu=[]
(使其成为一个包,以便
import
甚至尝试查找子模块),并注册挂钩。钩子本身拦截任何包加载,其中包的全名以“clr.”开头,使用
imp.new_module()
动态创建新模块,在
sys.modules
中注册,并使用pixie dust和rainbows用原始API中的类和方法填充它。代码如下:

clr.py test.py 总而言之,这一切似乎都很好。Python保证从左到右导入链中的模块,因此总是首先导入
clr
,并设置允许导入链其余部分的钩子


然而,我想知道我在这里做的是不是太过分了。毕竟,我正在安装一个全局钩子,任何模块导入都会调用它,即使我过滤掉了那些我不关心的东西。是否有某种方法可以安装一个钩子,该钩子只会在从我的特定包导入时调用,而不会在其他包中调用?或者上述方法是在Python中执行此类操作的正确方法?

总体而言,我认为您的方法看起来不错。我不担心它是“全局的”,因为关键是指定哪些路径应该由您处理。将此测试移到导入逻辑中只会使其变得不必要的复杂,因此这将由钩子的实现者来决定

只是一个小问题,也许您可以使用
sys.path\u hooks
?它似乎比
sys.meta\u path

sys.path\u hooks
是将被检入的可调用项列表 序列,以确定它们是否可以处理给定的路径项。这个 callable使用一个参数(路径项)调用。可调用的 如果无法处理路径项,则必须引发导入错误,并且 如果导入器对象可以处理路径项,则返回导入器对象


我确信您的代码只是一个示例,但只是为了检查一下——如果您实际上希望将CLR库移植到Python,那么应该使用IronPython@确实,我很了解铁蟒。但需要注意的是,它并不能完全解决您提到的问题——即,它要求您将Python代码移植到CLR,而不是使用来自CPython的CLR库(例如,与一些C库一起使用)。但是,是的,在这种情况下,CLR只是作为一个具体的、易于演示的示例使用;真正的东西是另外的东西。我已经看了
\uuu path\uu
sys.path
,它似乎是一个用于查找模块的文件路径列表。现在,从技术上讲,我可以将包的
\uuuuu path\uuuu
设置为我想要的任何内容,只要我还提供一个路径挂钩来处理相同的内容。但它闻起来像黑客(如果有人检查
\uuuuu路径\uuuuuuu
?他们被允许),而且似乎这并没有给我买任何超过使用
元路径的东西,因为过滤器仍然是全局的。另一方面,我现在必须确保正确维护我的所有包的
\uuuu path\uuuu
,以确保导入通过我的finder@Pavel:你的意思是
path\u hooks
?是的,注释引用了
path\u hooks
。通过阅读文档,似乎注册了它的处理程序将被输入
sys.path
(对于顶级包)或父包的
\uuuuuuu path\uuuuu
(对于子包)中的任何内容,因此我需要为动态投影的模块用伪值填充这些内容。我想我可以为
clr
模块提供一个真实的路径,然后假设它是一个包含投影模块子目录的目录。但我仍然不确定这比其他选项能给我带来什么。@Pavel:我也不确定:-)对导入挂钩不够熟悉,但从我对它们的文档和PEP的理解来看,您在这里所做的似乎是一个很好的用法
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())
import clr.Foo.Bar.Baz

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