Pav*_*aev 14 python import-hooks
我正在创建一个Python模块,将不同语言/框架提供的API映射到Python.理想情况下,我希望将其作为单个根包提供,该包公开辅助方法,并将该其他框架中的所有命名空间映射到Python包/模块.为方便起见,我们以CLR为例:
import clr.System.Data
import clr.System.Windows.Forms
Run Code Online (Sandbox Code Playgroud)
这clr是一个神奇的顶层包,它暴露了CLR命名空间System.Data和System.Windows.Forms子包/子模块(据我所知,一个包只是一个带有子模块/包的模块;它在其中有其他类型的成员仍然有效).
我已经阅读了PEP-302并编写了一个简单的原型程序,通过安装自定义meta_path钩子实现了类似的效果.该clr模块本身是一个适当的Python模块其中,导入时,套__path__ = [](使其成为一个包,以便import甚至试图为子模块在所有查找),并注册该钩.钩子本身拦截包的全名开始的任何包加载,使用"clr."动态创建新模块imp.new_module(),将其注册sys.modules,并使用pixie dust和rainbows用原始API中的类和方法填充它.这是代码:
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())
Run Code Online (Sandbox Code Playgroud)
import clr.Foo.Bar.Baz
clr.Foo.speak()
clr.Foo.Bar.speak()
clr.Foo.Bar.Baz.speak()
Run Code Online (Sandbox Code Playgroud)
总而言之,这似乎工作得很好.Python保证链中的模块从左到右导入,因此clr总是首先导入,并设置钩子,允许导入链的其余部分.
但是,我想知道我在这里做的事情是否有点过分.毕竟,我正在安装一个全局挂钩,即使我过滤掉了我不关心的那些,也会调用任何模块导入.是否有某种方法来安装一个钩子,只能从我的特定包中调用,而不是其他的?或者上面是在Python中做这种事情的正确方法?
总的来说,我认为你的方法看起来很好.我不担心它是"全局的",因为重点是指定你应该处理哪些路径.在导入逻辑中移动此测试只会使其不必要地复杂化,因此需要由钩子的实现者来决定.
只是一个小问题,也许你可以使用sys.path_hooks?它看起来有点"强大"sys.meta_path
sys.path_hooks是一个可调用的列表,将按顺序检查它们以确定它们是否可以处理给定的路径项.使用一个参数(路径项)调用callable.ImportError如果无法处理路径项,则callable必须引发,如果它可以处理路径项,则返回一个importer对象.
| 归档时间: |
|
| 查看次数: |
4737 次 |
| 最近记录: |