Plugin architecture - Plugin Manager vs inspecting from plugins import *

后端 未结 3 737
悲哀的现实
悲哀的现实 2020-12-29 14:55

I\'m currently writing an application which allows the user to extend it via a \'plugin\' type architecture. They can write additional python classes based on a BaseClass o

3条回答
  •  执笔经年
    2020-12-29 15:27

    The metaclass approach is useful for this issue in Python < 3.6 (see @quasoft's answer for Python 3.6+). It is very simple and acts automatically on any imported module. In addition, complex logic can be applied to plugin registration with very little effort. It requires:

    The metaclass approach works like the following:

    1) A custom PluginMount metaclass is defined which maintains a list of all plugins

    2) A Plugin class is defined which sets PluginMount as its metaclass

    3) When an object deriving from Plugin - for instance MyPlugin is imported, it triggers the __init__ method on the metaclass. This registers the plugin and performs any application specific logic and event subscription.

    Alternatively if you put the PluginMount.__init__ logic in PluginMount.__new__ it is called whenver a new instance of a Plugin derived class is created.

    class PluginMount(type):
        """
        A plugin mount point derived from:
            http://martyalchin.com/2008/jan/10/simple-plugin-framework/
        Acts as a metaclass which creates anything inheriting from Plugin
        """
    
        def __init__(cls, name, bases, attrs):
            """Called when a Plugin derived class is imported"""
    
            if not hasattr(cls, 'plugins'):
                # Called when the metaclass is first instantiated
                cls.plugins = []
            else:
                # Called when a plugin class is imported
                cls.register_plugin(cls)
    
        def register_plugin(cls, plugin):
            """Add the plugin to the plugin list and perform any registration logic"""
    
            # create a plugin instance and store it
            # optionally you could just store the plugin class and lazily instantiate
            instance = plugin()
    
            # save the plugin reference
            cls.plugins.append(instance)
    
            # apply plugin logic - in this case connect the plugin to blinker signals
            # this must be defined in the derived class
            instance.register_signals()
    

    Then a base plugin class which looks like:

    class Plugin(object):
        """A plugin which must provide a register_signals() method"""
        __metaclass__ = PluginMount
    

    Finally, an actual plugin class would look like the following:

    class MyPlugin(Plugin):
        def register_signals(self):
            print "Class created and registering signals"
    
        def other_plugin_stuff(self):
            print "I can do other plugin stuff"
    

    Plugins can be accessed from any python module that has imported Plugin:

    for plugin in Plugin.plugins:
        plugin.other_plugin_stuff()
    

    See the full working example

提交回复
热议问题