![](/img/trans.png)
[英]Insert Python Packages from Separate Directory into a different Namespace
[英]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__.py
中myframework/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文件将子模块的导入调用重定向到您希望它接收的子模块,但这有点乱。
有关导入挂钩的更多信息,请访问:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.