繁体   English   中英

将单独的python包放入相同的命名空间?

[英]Putting separate python packages into same namespace?

我正在开发一个python框架,它将“插件”写成单独的包。 即:

import myframework
from myframework.addons import foo, bar

现在,我正在尝试安排的是这些插件可以与核心框架分开分发并注入myframework.addons命名空间。

目前,我对此的最佳解决方案如下。 将部署一个附加组件(最有可能部署到{python_version}/site-packages/如下所示:

fooext/
fooext/__init__.py
fooext/myframework/
fooext/myframework/__init__.py
fooext/myframework/addons/
fooext/myframework/addons/__init__.py
fooext/myframework/addons/foo.py

fooext/myframework/addons/__init__.py将具有pkgutil路径扩展代码:

import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

问题是,为了使这个工作,PYTHONPATH需要有fooext/ ,但它唯一的东西是父安装目录(很可能是上面提到的site-packages )。

解决方法是在myframework/addons/__init__.pymyframework/addons/__init__.py额外的代码, myframework/addons/__init__.py代码将转换sys.path并查找带有myframework子包的任何模块,在这种情况下,它将它添加到sys.path并且一切正常。

我的另一个想法是将插件文件直接写入myframework/addons/ install位置,但随后它会使开发和部署的命名空间不同。

是否有更好的方法来实现这一点,或者可能采用不同的方法解决上述分配问题?

是否有更好的方法来实现这一点,或者可能采用不同的方法解决上述分配问题?

有可能。 Python的模块/包设置通常很难动态地篡改,但它的对象/类系统是以明确定义的方式打开和扩展的。 当模块和包不具备您需要很好地封装项目的功能时,您可以使用类来代替。

例如,您可以在完全不同的包中使用扩展功能,但允许它通过特定接口将类注入到基本框架中。 例如。 包含基本应用程序包装器的myframework / _ _ init _ _.py:

class MyFramework(object):
    """A bare MyFramework, I only hold a person's name
    """
    _addons= {}
    @staticmethod
    def addAddon(name, addon):
        MyFramework._addons[name]= addon

    def __init__(self, person):
        self.person= person
        for name, addon in MyFramework._addons.items():
            setattr(self, name, addon(self))

然后你可以在myexts / helloer.py中有扩展功能,它保持对其“所有者”或“外部”MyFramework类实例的引用:

class Helloer(object):
    def __init__(self, owner):
        self.owner= owner
    def hello(self):
        print 'hello '+self.owner.person

import myframework
myframework.MyFramework.addAddon('helloer', Helloer)

所以现在如果你只是“导入myframework”,你只能获得基本功能。 但如果您还“导入myexts.helloer”,您还可以调用MyFramework.helloer.hello()。 当然,您还可以为插件定义协议,以便与基本框架行为相互作用。 您还可以执行内部类的操作,如框架的子类可以覆盖以进行自定义,而不必使用可能影响其他应用程序的monkey-patch类,如果您需要这种复杂程度。

像这样的封装行为可能很有用,但通常很烦人的工作是调整你已经适应这个模型的模块级代码。

Setuptools能够按名称查找包“入口点”(函数,对象等)。 Trac使用这种机制来加载它的插件 ,它运行良好。

命名空间有一个全新的设置。 看看Packaging命名空间包 简而言之,根据您希望代码的向后兼容程度,您有三种选择。 还有一个相关的PEP,取代了其他答案中提到的那些: PEP 420

这听起来像你所追求的东西可以完全与导入钩子完成。

这是一种编写自定义加载代码的方法,它可以与包(或在您的情况下是框架)相关联,以执行所有子包和模块的加载,而不是使用python的默认加载机制。 然后,您可以将加载程序作为基本软件包或在框架下安装在site-packages中。

当发现一个包与加载器相关联时(如果需要可以简单地将其硬编码到相对路径),它将始终使用加载器来加载所有加载项。 这样做的好处是不需要任何PYTHONPATH的摆弄,这通常值得保持尽可能短。

替代方法是使用init文件将子模块的导入调用重定向到您希望它接收的子模块,但这有点乱。

有关导入挂钩的更多信息,请访问:

http://www.python.org/dev/peps/pep-0302/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM