The metaclass approach is useful for this problem in Python <3.6 (see @quasoft answer for Python 3.6+). It is very simple and acts automatically on any imported module. In addition, complex logic can be applied to register a plugin with minimal effort. This requires:
The metaclass approach works as follows:
1) A custom metaclass PluginMount that supports a list of all plugins
2) A Plugin class is defined that sets PluginMount as its metaclass
3) When an object obtained from Plugin is imported - for example, MyPlugin , it runs the __init__ method in the metaclass. This registers the plugin and performs any specific application logic and event subscription.
Alternatively, if you put the PluginMount.__init__ in the PluginMount.__new__ , it is called when a new instance of the 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'):
Then the plugin base class is as follows:
class Plugin(object): """A plugin which must provide a register_signals() method""" __metaclass__ = PluginMount
Finally, the actual plugin class will look like this:
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"
Access to plugins can be obtained from any python module that imported Plugin :
for plugin in Plugin.plugins: plugin.other_plugin_stuff()
See full working example.