2011-01-24 60 views
10

我正在編寫一個應用程序。沒有花哨的圖形用戶界面:不管什麼,只是一個普通的舊控制檯應用該應用程序可以稱爲App,需要能夠在啓動時加載插件。因此,很自然,我創建了一個類的插件繼承:動態導入模塊,然後從所述模塊實例化具有某個基類的對象

class PluginBase(object): 
    def on_load(self): 
     pass 
    def on_unload(self): 
     pass 
    def do_work(self, data): 
     pass 

的想法是,在啓動時,應用程序將通過當前目錄,包括子目錄走,尋找包含類本身是的子模塊PluginBase

更多代碼:

class PluginLoader(object): 
    def __init__(self, path, cls): 
     """ path=path to search (unused atm), cls=baseclass """ 
     self.path=path 
    def search(self): 
     for root, dirs, files in os.walk('.'): 
      candidates = [fname for fname in files if fname.endswith('.py') \ 
            and not fname.startswith('__')] 
     ## this only works if the modules happen to be in the current working dir 
     ## that is not important now, i'll fix that later 
     if candidates: 
      basename = os.path.split(os.getcwd())[1] 
      for c in candidates: 
       modname = os.path.splitext(c)[0] 
       modname = '{0}.{1}'.format(basename, modname) 
       __import__(mod) 
       module = sys.modules[mod] 

經過search最後一行,我想以某種方式一)發現新加載的模塊中的所有類,b)檢查如果一個或多個這些類是子類PluginBase和c)(如果b)實例化那些/這些類並添加到應用程序的加載模塊列表。

我試過issubclass和其他的各種組合,然後是激烈的dir:ing和約一個小時的慌亂Google搜索。我找到了類似的方法,我的here,我試着複製粘貼,但得到一個錯誤,說Python不支持通過文件名導入,在這一點上我有點失去了專注力,因此,這篇文章被寫了。

我在我的智慧結束在這裏,所有的幫助表示讚賞。

回答

4

你可能會做這樣的事情:

for c in candidates: 
    modname = os.path.splitext(c)[0] 
    try: 
     module=__import__(modname) #<-- You can get the module this way 
    except (ImportError,NotImplementedError): 
     continue 
    for cls in dir(module):   #<-- Loop over all objects in the module's namespace 
     cls=getattr(module,cls) 
     if (inspect.isclass(cls)    # Make sure it is a class 
      and inspect.getmodule(cls)==module # Make sure it was defined in module, not just imported 
      and issubclass(cls,base)):   # Make sure it is a subclass of base 
      # print('found in {f}: {c}'.format(f=module.__name__,c=cls)) 
      classList.append(cls) 

爲了測試上面,我不得不修改您的代碼位;下面是完整的腳本。

import sys 
import inspect 
import os 

class PluginBase(object): pass 

def search(base): 
    for root, dirs, files in os.walk('.'): 
     candidates = [fname for fname in files if fname.endswith('.py') 
         and not fname.startswith('__')] 
     classList=[] 
     if candidates: 
      for c in candidates: 
       modname = os.path.splitext(c)[0] 
       try: 
        module=__import__(modname) 
       except (ImportError,NotImplementedError): 
        continue 
       for cls in dir(module): 
        cls=getattr(module,cls) 
        if (inspect.isclass(cls) 
         and inspect.getmodule(cls)==module 
         and issubclass(cls,base)): 
         # print('found in {f}: {c}'.format(f=module.__name__,c=cls)) 
         classList.append(cls) 
     print(classList) 

search(PluginBase) 
+0

它的工作幾乎完美,謝謝!我添加了`和cls .__ name__!= base .__ name__`,以避免將基類添加到子類列表中。 – manneorama 2011-01-25 21:03:16

0

你可以使用execfile()而不是導入一個指定的命名空間字典,然後迭代該名稱空間與issubclass等?

3

如果您在插件編寫器上強制實施某些約束,例如所有插件都必須是包含返回插件實例的load_plugin(app, config)函數的包,那麼您會更容易。然後你所要做的就是嘗試導入這些包並運行該函數。

2

這裏是註冊插件的元分類器的方法:

定義PluginBasePluginType類型。 PluginType會自動註冊plugins集合中的任何實例(類)。

plugin.py:

plugins=set() 
class PluginType(type): 
    def __init__(cls, name, bases, attrs): 
     super(PluginType, cls).__init__(name, bases, attrs) 
     # print(cls, name,cls.__module__) 
     plugins.add(cls) 

class PluginBase(object): 
    __metaclass__=PluginType 
    pass 

這是用戶寫的部分。請注意,這裏沒有特別之處。

pluginDir/myplugin。潘岳:

下面是搜索功能可能是什麼樣子:

test.py:

import plugin 
import os 
import imp 

def search(plugindir): 
    for root, dirs, files in os.walk(plugindir): 
     for fname in files: 
      modname = os.path.splitext(fname)[0] 
      try: 
       module=imp.load_source(modname,os.path.join(root,fname)) 
      except Exception: continue 

search('pluginDir') 
print(plugin.plugins) 

運行test.py產量

set([<class 'myplugin.Foo'>]) 
相關問題