2009-01-18 78 views
12

我正在開發一個python框架,將「附加」作爲單獨的包編寫。即:將單獨的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)。

解決方法是在中有額外的代碼,它會通過sys.path來查找任何帶有myframework子包的模塊,在這種情況下,它會將其添加到sys.path並且一切正常。

我的另一個想法是直接將插件文件寫入myframework/addons/安裝位置,但這樣會使開發和部署的名稱空間不同。

有沒有更好的方法來完成這個或者完全不同的方法來解決上述分配問題?

回答

4

有沒有更好的方式來做到這一點或者一個不同的方法對上述分配問題完全?

可能。 Python的模塊/軟件包設置通常很難以動態方式篡改,但其對象/類系統是以明確定義的方式開放和可擴展的。當模塊和程序包不完全具備需要封裝好項目的功能時,可以使用類。

例如,您可以將擴展功能放在一個完全不同的包中,但允許它通過特定的接口將類注入到基本框架中。例如。 myframework/_ _ _初始化包含基本應用程序包裝_.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()。當然,您也可以定義插件的協議,以便與基本框架行爲互相交互。你也可以做類似內部類的事情,如果你需要這樣的複雜性,那麼框架的一個子類可以覆蓋它來自定義,而不需要可能影響其他應用程序的猴子補丁類。

像這樣的封裝行爲可能很有用,但是爲了適應你已經適合這個模型的模塊級代碼通常是很煩人的工作。

+0

請參閱下面的亞歷克的答案。 Setuptools有這方面的東西,稱爲entry_points。 – 2014-12-28 22:33:25

0

這聽起來像你以後可以用導入鉤很整齊地完成。

這是一種編寫自定義加載代碼的方法,它可以與一個包(或者在你的情況下是一個框架)相關聯,以執行加載所有子包和模塊,而不是使用python的默認加載機制。然後,您可以將加載程序作爲基礎軟件包或在您的框架下安裝到網站軟件包中。

當發現一個包與加載器相關聯時(如果需要,它可以簡單地硬編碼爲相對路徑),然後它將總是使用加載器加載所有加載項。這具有不需要擺動PYTHONPATH的優點,通常值得儘可能短。

另一種方法是使用init文件將子模塊的導入調用重定向到您希望它拾取的那個,但這有點麻煩。進口鉤

更多信息可以在這裏找到:

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

3

Setuptools能夠按名稱查找包「入口點」(函數,對象,無論)。 Trac使用這種機制到load its plugins,它運作良好。

+0

我會投票給亞歷克的建議高於任何其他人。入口點就是爲此設計的,文檔中有一個很好的dyanmic插件加載示例:https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins – 2014-12-28 22:32:49

0

沒有爲命名空間完全新的設置。看看Packaging namespace packages。簡而言之,您有三種選擇,基於您想要代碼的向後兼容程度。還有一個相關的PEP,取代了其他答案中提到的那些:PEP 420

相關問題