Plugins

What’s a Plugin ?

A plugin is a kabaret extension which:
  • Is packaged to an index (like PyPI).
  • Is automatically active in all Kabaret sessions.
  • Reports what it provides.
  • Is listed in the Plugin view.
  • Can be deactivated using an environment variable.

But most importantly, a plugin is a kabaret extension that the user can install and start using by issuing a single command:

pip install my_kabaret_extension

Plugin Creation

To create a plugin you just need to implement some of the supported “hooks” using the kabaret.app.plugin decorator.

Your hooks implementation must be contained in a module, a static class, or an instance (anything that can be treated as a namespace in fact).

Here is a simple example using a module namespace, all hooks defined here will form the “my_stuff” plugin:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
'''
Inside "my_stuff.py", this is the "my_stuff" plugin.
'''

from pyqt import QtCore

from kabaret.app import plugin
from .my_stuff.my_view import MyView

@plugin
def install_views(session):
    if not session.is_gui():
        return

    type_name = session.register_view_type(MyView)
    session.add_view(
        type_name,
        hidden=False,
        area=QtCore.Qt.RightDockWidgetArea
    )

And here is an example using classes in order to define too plugins in the same module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
'''
Inside "best_plugins.py", there is 2 plugins defined.
'''
from pyqt import QtCore

from kabaret.app import plugin
from .best_view_ever import BestViewEver
from .best_pipe_ever import BestPipelineEver


class BestView:
    """This is the `BestView` plugin"""

    @plugin
    def install_views(session):
        if not session.is_gui():
            return

        type_name = session.register_view_type(MyView)
        session.add_view(
            type_name,
            hidden=False,
            area=QtCore.Qt.RightDockWidgetArea
        )

class BestPipe:
    """This is the "BestPipe" plugin"""

    @plugin
    def get_project_types(session):
        return [BestPipelineEver]

Pluggable Hooks

Here is the exhaustive list of plugin hooks.

Note that you must respect the given signature.

  • install_actors(session)
    This let your plugin install some Actor in the session.
  • install_views(session)
    This let your plugin install some View in the session.
  • install_resources(session)
    Kabaret resources don’t have an installation procedure since a simple import is enough. But doing you resources import in this hook ensures that it will be done before other hooks get triggered.
  • install_editors(session)
    The editor factory is static so you can install your editors with a simple import, but using this hook will ensures you that the editors are registered before any view is created.
  • get_project_types(session)
    Here your plugin can return a list of kabaret.flow.Object that are ment to be used as Project structure. Some other extension may use this information to present a list of available project types to the user.
  • get_flow_objects(session)
    Here your plugin can return a list of kabaret.flow.Object that provide packaged features. This is purely informative since you will choose to use them or not in your flow. But this has the advantage of listing in the Plugin View the Injection points defined in those objects.

Plugin Activation

Plugins are activated by package distribution entry points in the “kabaret.plugin” group.

To activate the 3 plugins defined in the examples above, your setup.py would typically look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Inside your extension's "setup.py"

from setuptools import setup, find_packages

setup(
    name="my_extension_name",
    ...
    entry_points = {
        "kabaret.plugin":[
            "my_extension_name.my_stuff = my_extension_name.my_stuff",
            "my_extension_name.best_view = my_extension_name.best_plugins:BestView",
            "my_extension_name.best_pipe = my_extension_name.best_plugins:BestPipe",
        ],
    }

Plugin Manual Activation

If some of your extensions are not packaged and installable via pip, you won’t get a chance to define entry points.

In this case, creating a custom session class will give you the opportunity to register you plugins programatically. Just override the register_plugins() method and use the provided plugin_manager to register your plugin modules/classes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# In "run_my_standalone_kabaret.py", a script launching A
# standalone kabaret application

from kabaret.app.ui import gui

from my_extension_name import my_stuff
from my_extension_name.best_plugins import BestView, BestPipe

class MyGUISession(gui.KabaretStandaloneGUISession):

    def register_plugins(self, plugin_manager):
        plugin_manager.register(my_stuff)
        plugin_manager.register(BestView)
        plugin_manager.register(BestPipe)

That being said, packaging your code is a good thing for many reasons and you should consider doing it ;)

Plugin Desactivation

All installed plugins are active by default, but sometime you will want to block some of them. This is done with the KABARET_BLOCKED_PLUGINS environment variable.

Using the example above, you can desactivate the “my_stuff” and the “BestView” plugins like this:

export KABARET_BLOCKED_PLUGINS="my_stuff BestView"
python run_my_standalone_kabaret

Note that this is not intended to be used as a “plugin list management” but rather for debuging and corner cases. If you want to manage different sets of plugins, you should use different virtualenvs. They are designed for this and with the –editable option of the pip install command, you will have the best control and versatility over your plugins installation.

Plugin Order

You will sometimes need a plugin to act depending on other plugins.

A classic example would be a “default plugin” that would install a View type only if no other plugin already did.

A simple approach is to test for the view type name being registered, but this is not enough until you can be sure that this plugin will be called after any other plugin wanting to install this view type.

To affect the plugin call order and acheive this, you must configure your plugin with the trylast option. All “trylast” plugins are guaranted to be called after plugin without it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from kabaret.app import plugin
from my_custom_stuff import FlowView

@plugin(trylast=True)
def install_views(session):
    if session.has_view_type('Flow'):
        # some other plugin took care
        # of this, let's bail out.
        return

    # The Flow view is mandatory, let's
    # install it and create one:
    session.register_view_type(FlowView)
    session.add_view(type_name)

Similarly, you can use the tryfirst option to ensure a plugin is called before any plugin without the tryfist option.