Plugins¶
Plugins are used by groundwork to load specific functions into a groundwork application.
In most cases a plugin should have a functional focus, like providing some documentation about signals or providing some commands to the user to handle specific tasks on data.
A plugin can be activated and deactivated during runtime. And it can be loaded from python packages or from own code.
The following rules apply for each groundwork plugin.
- A plugin contains code, documentation and tests.
- A plugin provides routines for activation and deactivation during runtime.
- A plugin inherits directly or indirectly from
GwBasePattern
.
During development of a plugin, patterns can be used to extend its functionality or grant access to specific objects, like a database engine to perform database actions.
Registration¶
The registration of a plugin must happen by using the application:
from groundwork import App
my_app = App()
my_app.plugins.activate(["GwPluginInfo"])
For more information please read Plugin registration of the chapter Application.
Activation and Deactivation¶
Plugins can be activated and deactivated during runtime. There are two ways of doing this:
- By the
activate()
/deactivate()
function, accessible bymy_app.plugins.activate()
ormy_app.plugins.deactivate()
.- By the activation/deactivation function of the plugin itself, accessible by
my_plugin.activate()
ormy_plugin.deactivate()
.
For a code example, please take a look into Plugin activation and Plugin deactivation of the application documentation.
Development of own plugins¶
To start the development of own plugins, simply create a new class and inherit from
GwBasePattern
:
from groundwork.patterns import GwBasePattern
class MyPlugin(GwBasePattern):
def __init__(self, app, **kwargs):
self.name = "My Plugin"
super().__init__(app, **kwargs)
def activate(self): pass
def deactivate(self): pass
Warning
It is very important to call the __init__
routine of parent classes. Otherwise they can’t deliver functions
and objects, which you may need. Also no signals are registered, which inform interested functions when your
plugin gets activated or deactivated. So no automatic cleanup would happen, like erasing all registered
commands of your plugin.
Also make sure that your __init__
can handle app as the first argument and
additional, optional keyword arguments.
Provided variables¶
The groundwork GwBasePattern
creates the following variables for your
plugin and makes them directly available:
- self.path: The absolute path of the python-file, which contains your plugin (directory + file name)
- self.dir: The absolute directory, which contains your plugin (directory only)
- self.file: The name of the file, which contains your plugin (file name only)
- self.version: An initial version (0.0.1), if this was not set by your plugin during initialisation
- self.active: True, if the plugin got activated.
- self.needed_plugins: Empty tuple, if it was not set by your plugin during initialisation
Using signals and receivers¶
You are free to add signals or connect receivers to them:
from groundwork.patterns import GwBasePattern
class MyPlugin(GwBasePattern):
def __init__(self, app, **kwargs):
self.name = "My Plugin"
super().__init__(app, **kwargs)
def activate(self):
self.signals.register(signal="My signal",
description="Informing about something")
self.signals.connect(receiver="My signal receiver",
signal="My signal",
function=self.fancy_stuff,
description="Doing some fancy stuff")
def fancy_stuff(plugin, **kwargs):
print("FANCY STUFF!!! " * 50)
For more details about signals, please read Signals and Receivers.
Note
Each plugin sends automatically signals when it gets activated or deactivated. The used signals are: plugin_activate_pre, plugin_activate_post, plugin_deactivate_pre and plugin_deactivate_post.
Please see Signals and Receivers for more information.
Using patterns¶
Patterns can be used to extend your plugin with new functions and objects.
groundwork itself provides 6 patterns:
You can load multiple patterns into your plugin:
from groundwork.patterns import GwCommandsPattern, GwDocumentsPattern, GwSharedObjectsPattern
# GwBasePattern is no longer needed, because the used patterns already inherit from it.
class MyPlugin(GwCommandsPattern, GwDocumentsPattern, GwSharedObjectsPattern):
def __init__(self, app, **kwargs):
self.name = "My Plugin"
super().__init__(app, **kwargs)
def activate(self):
self.commands.register(...)
self.documents.register(...)
self.shared_objects.register(...)
For more information about these patterns, please read the related chapters: Commands, Documents, Recipes, Shared Objects and Threads.
Logging¶
Each plugin has its own logger, which name is the name of the plugin. It is accessible via self.log
inside a plugin
class:
from groundwork.patterns import GwBasePattern
class MyPlugin(GwBasePattern):
def __init__(self, app, **kwargs):
self.name = "My Plugin"
super().__init__(app, **kwargs)
self.log.info("Initialisation done for %s" % self.name)
def activate(self):
self.log.debug("Starting activation")
self.log.info("Activation done")
For each logger, and therefore for each plugin, it is possible to register handlers to monitor specific plugins and log messages in detail.
For instance: Store all messages of “My Plugin” inside a file called “my_plugin.log”. All other messages go to “app.log”.
For details how to configure groundworks logging, please see logging configuration.
Plugin dependencies¶
A plugin can have dependencies to other plugins and it needs to be sure that these plugins are activated in the current app.
Therefore a plugin can specify the names of needed plugins and groundwork cares about their activation:
from groundwork.patterns import GwBasePattern
class MyPlugin(GwBasePattern):
def __init__(self, app, **kwargs):
self.name = "My Plugin"
self.needed_plugins = ("AnotherPlugin", "AndAnotherPlugin")
super().__init__(app, **kwargs)
During plugin activation, groundwork does the following:
- Read in
self.needed_plugins
- For each plugin name
- Check, if a plugin with this name exists in app.plugins (objects/instantiated plugins)
- If yes: activate it instantly (if not done yet)
- If no: check for plugin classes with this name in app.plugin.classes (classes, not instantiated)
- If yes: Instantiate and activate it
- If not: Throw error