2011-01-24 17 views
10

Tôi đang viết một ứng dụng. Không có giao diện ưa thích: s hoặc bất cứ thứ gì, chỉ là một ứng dụng giao diện điều khiển cũ đơn giản. Ứng dụng này, cho phép gọi nó là Ứng dụng, cần có khả năng tải các plugin khi khởi động. Vì vậy, một cách tự nhiên, tôi đã tạo ra một lớp học cho các plugin để kế thừa từ:Nhập động các mô-đun theo sau là sự khởi tạo của các đối tượng với một baseclass nhất định từ các mô-đun đã nêu

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

Ý tưởng được rằng khi khởi động, ứng dụng sẽ đi bộ qua các thư mục hiện hành, bao gồm subdirs, tìm kiếm các module chứa các lớp học mà bản thân chúng là lớp con của PluginBase.

Nhiều mã:

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] 

Sau đó dòng cuối cùng trong search Tôi muốn bằng cách nào đó a) tất cả các lớp trong mô-đun mới nạp, b) kiểm tra nếu một hoặc nhiều hơn những lớp là lớp con của PluginBase và c) (nếu b) khởi tạo/các lớp đó và thêm vào danh sách các mô-đun đã tải của ứng dụng.

Tôi đã thử các kết hợp khác nhau của issubclass và các loại khác, theo sau là khoảng thời gian cường độ cao dir: ing và khoảng một giờ googling hoảng sợ. Tôi đã tìm thấy một cách tiếp cận tương tự như của tôi here và tôi đã thử sao chép và dán nhưng có lỗi nói rằng Python không hỗ trợ nhập bằng tên tệp, tại thời điểm đó tôi bị mất tập trung và kết quả là, bài đăng này đã được viết.

Tôi đang kết thúc ở đây, tất cả trợ giúp đều được đánh giá cao.

Trả lời

4

Bạn có thể làm điều gì đó như thế này:

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) 

Để kiểm tra ở trên, tôi đã phải sửa đổi mã của bạn một chút; bên dưới là toàn bộ tập lệnh.

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

Nó làm việc gần như hoàn hảo, cảm ơn bạn! Tôi đã thêm 'và cls .__ name__! = Base .__ name__' để tránh lớp cơ sở đang được thêm vào danh sách các lớp con. – manneorama

0

Bạn có thể sử dụng execfile() thay vì nhập bằng dict không gian tên được chỉ định, sau đó lặp qua không gian tên đó với issubclass, v.v.

3

Bạn sẽ thực hiện việc này dễ dàng hơn nhiều nếu bạn buộc một số ràng buộc đối với người viết plugin, ví dụ tất cả các plugin phải là các gói chứa hàm load_plugin(app, config) trả về phiên bản Plugin. Sau đó, tất cả những gì bạn phải làm là thử nhập các gói này và chạy hàm.

2

Dưới đây là một cách meta-classier để đăng ký bổ sung:

Xác định PluginBase là loại PluginType. PluginType tự động đăng ký bất kỳ cá thể (lớp) nào trong tập hợp 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 

Đây là phần mà người dùng viết. Lưu ý rằng không có gì đặc biệt ở đây.

pluginDir/myplugin.py:

Dưới đây là những gì các chức năng tìm kiếm có thể trông giống như:

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) 

Chạy suất test.py

set([<class 'myplugin.Foo'>]) 
Các vấn đề liên quan