将单独的python包放入同一名称空间?

将单独的python包放入同一名称空间?,python,namespaces,distribution,extend,Python,Namespaces,Distribution,Extend,我正在开发一个python框架,该框架将“插件”编写为单独的包。即: import myframework from myframework.addons import foo, bar 现在,我试图安排的是,这些插件可以与核心框架分开分发,并注入myframework.addons名称空间 目前我的最佳解决方案如下。将部署一个附加组件(很可能部署到{python\u version}/site-packages/中,如下所示: fooext/ fooext/__init__.py fooex

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

import myframework
from myframework.addons import foo, bar
现在,我试图安排的是,这些插件可以与核心框架分开分发,并注入
myframework.addons
名称空间

目前我的最佳解决方案如下。将部署一个附加组件(很可能部署到
{python\u 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/
,但是它唯一需要的是父安装目录(很可能是上述
站点包

解决这个问题的方法是在
myframework/addons/\uuuuu init\uuuuuu.py
中添加额外的代码,这将转换
sys.path
,并查找带有myframework子包的任何模块,在这种情况下,它将其添加到
sys.path
,一切正常

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


有没有更好的方法来完成这一点,或者可能有一种完全不同的方法来解决上述分发问题?

听起来,您想要的东西可以通过导入挂钩非常巧妙地完成

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

当发现一个包与加载程序关联时(如果需要,可以简单地硬编码到相对路径),它将始终使用加载程序加载所有附加组件。例如,这样做的优点是不需要对PYTHONPATH进行任何修改,这通常值得尽可能短地保存

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

有关导入挂钩的更多信息,请参见:

有没有更好的方法来实现这一点,或者可能有一种不同的方法来解决上述分配问题

可能。Python的模块/包设置通常很难像这样动态修改,但它的对象/类系统是开放的,并且可以以定义良好的方式进行扩展。当模块和包不具备很好地封装项目所需的功能时,可以改为使用类

例如,您可以在一个完全不同的包中使用扩展功能,但允许它通过一个特定的接口将类注入到您的基本框架中/_​​_​初始化​_​​_.包含基本应用程序包装的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()。当然,您还可以为插件定义协议,以便与基本框架行为以及彼此进行交互。您还可以执行诸如内部类之类的操作。如果您需要该级别的复杂性,框架的子类可以覆盖以进行自定义,而无需对可能影响其他应用程序的类进行修补


封装这样的行为可能很有用,但调整模块级代码以适应此模型通常是一项烦人的工作。

Setuptools能够按名称查找包的“入口点”(函数、对象等)。Trac使用此机制,并且工作良好。

请参阅命名空间包:

或在设置工具中:


名称空间有一个全新的设置。请看一下。简而言之,根据您希望代码向后兼容的程度,您有三个选项。还有一个相关的PEP,它取代了其他答案中提到的选项:。

我投票支持Alec的建议,高于其他任何建议。入口点是为r正是这一点,文档中有一个很好的动态插件加载示例:请参见下面的Alec的答案。Setuptools有一个与此相关的东西,称为入口点。