LogoLogo
Official SiteAPI Reference
stable/3.0
stable/3.0
  • Cement Developer Guide
  • Release Information
    • What's New!
    • Upgrading
    • ChangeLog
    • Deprecations
  • Getting Started
    • Installation
    • Developer Tools
    • Framework Overview
    • Beginner Tutorial
      • Part 1: Creating Your First Project
      • Part 2: Adding Features
      • Part 3: Extending a Project
      • Part 4: Making Everything Legit
  • Core Foundation
    • Interfaces and Handlers
    • Hooks
    • Configuration Settings
    • Arguments
    • Logging
    • Controllers
    • Output Rendering
    • Caching
    • Mail Messaging
    • Framework Extensions
    • Application Plugins
    • Templating
  • Utilities
    • Filesystem
    • Shell
    • Miscellaneous
  • Extensions
    • Alarm
    • Argparse
    • Colorlog
    • ConfigParser
    • Daemon
    • Dummy
    • Generate
    • Jinja2
    • Json
    • Logging
    • Memcached
    • Mustache
    • Plugin
    • Print
    • Redis
    • Scrub
    • SMTP
    • Tabulate
    • Yaml
    • Watchdog
  • Additional Topics
    • Extending The App Object
    • Unit Testing
    • Cleanup
    • Signal Handling
    • Pipenv
    • Autocomplete
    • Profiling with cProfile
    • Debugging with VSCode
  • Environment Variables
  • Terminology
  • Contributing
  • Privacy Policy
Powered by GitBook
On this page
  • Introduction to the Plugin Interface
  • Configuration
  • Application Configuration Settings
  • Application Meta Options:
  • Working with Plugins
  • Creating a Plugin
  • Loading a Plugin
  • Single File Plugins vs. Plugin Directories
  • Loading Templates From Plugin Directories
  • Creating a Plugin Handler
  1. Core Foundation

Application Plugins

PreviousFramework ExtensionsNextTemplating

Last updated 4 years ago

Introduction to the Plugin Interface

Cement defines a Plugin Interface, as well as the default that implements the interface.

Cement often includes multiple handler implementations of an interface that may or may not have additional features or functionality than the interface requires. The documentation below only references usage based on the interface and default handler (not the full capabilities of an implementation).

Cement Extensions that Provide Plugin Handlers:

API References:

Configuration

Application Configuration Settings

The following settings under the application's primary configuration section modify plugin handling:

Setting

Description

plugin_dir

A directory path where plugin code can be found. Will be prepended to App.Meta.plugin_dirs

Application Meta Options:

The following options under modify plugin handling:

Option

Description

plugins

A hardcoded list of plugins to load. In general, application plugins should be dynamically enabled/disabled via the application's configuration files. However, some application designs may prefer to always load specific builtin plugins. Default: []

config_dirs

Plugin configuration files are loaded from any discovered application configuration directories.

plugin_module

A python module (dotted import path) where plugin code can be loaded from instead of external directories (builtin plugins shipped with the application code). Default: myapp.plugins

plugin_dirs

Working with Plugins

The plugin handler can be used to access information about loaded plugins, as well as manually loading plugins if necessary.

from cement import App

with App('myapp') as app:
    # list loaded plugins
    app.plugin.get_loaded_plugins()

    # list enabled plugins
    app.plugin.get_enabled_plugins()

    # list disabled plugins
    app.plugin.get_disabled_plugins()

    # load a plugin
    app.plugin.load_plugin('myplugin')

    # load a list of plugins
    app.plugin.load_plugins(['myplugin1',
                             'myplugin2'])

Creating a Plugin

The plugin system is a mechanism for dynamically loading code to extend the functionality of a specific application. In general, this includes the registration of interfaces, handlers, and/or hooks but can include controllers, command-line options, or anything else.

$ cement generate plugin /path/to/myapp/plugins

This will produce an example plugin directory like the following:

├── __init__.py
├── controllers
│   ├── __init__.py
│   └── myplugin.py
└── templates
    ├── __init__.py
    └── plugins
        └── myplugin
            └── command1.jinja2
import os
from .controllers.myplugin import MyPlugin

def add_template_dir(app):
    path = os.path.join(os.path.dirname(__file__), 'templates')
    app.add_template_dir(path)

def load(app):
    app.handler.register(MyPlugin)
    app.hook.register('post_setup', add_template_dir)

Plugins can provide anything from defining interfaces, registering hooks, or even adding command line sub-commands and arguments. The only thing required to make up a plugin is a load() function in myplugin.py or myplugin/__init__.py files.

You will notice that plugins are essentially the same as framework extensions. The difference is found both in when/how the code is loaded, as well as the purpose of that code.

Framework extensions add functionality to the framework for the application to utilize, whereas application plugins extend the functionality of the application itself.

Loading a Plugin

Plugin modules are discovered and loaded in the following order:

  • From directories listed in App.Meta.plugin_dirs

  • From the python path defined in App.Meta.plugin_module

In order for the framework to know about a plugin, it must be defined in the application's configuration settings under its designated section of plugin.myplugin. This configuration block can live in any application configuration file, including files loaded from configuration dirs (ex: /etc/myapp/plugin.d/myplugin.conf).

myapp.py
from cement import App

class MyApp(App):
    class Meta:
        label = 'myapp'
        plugin_dirs = ['./plugins']

with MyApp() as app:
    app.run()
plugins/myplugin.py
def load(app):
    print('Inside MyPlugin!')
$ python myapp.py
Inside MyPlugin!
...

If a plugin configuration is found, its settings will be loaded into the app. However, the plugin will only be loaded if it is enabled:

[myapp]
# ...

[plugin.myplugin]
enabled: true

Single File Plugins vs. Plugin Directories

As of Cement 2.9.x, plugins can be either a single file (i.e myplugin.py) or a python module directory (i.e. myplugin/__init__.py). Both will be loaded and executed the exact same way.

One caveat, however, is that the submodules referenced from within a plugin directory must be relative paths. For example:

myplugin/__init__.py
from .controllers import MyPluginController

def load(app):
    app.handler.register(MyPluginController)

This will ensure that Python will properly load the sub-modules regardless of where they live on the filesystem (or within a project's own modules, etc).

Loading Templates From Plugin Directories

In order for a plugin to use its own template files, its templates directory first needs to be registered with the app. We accomplish this with a post_setup hook:

myplugin/__init__.py
import os

def add_template_dir(app):
    path = os.path.join(os.path.basename(self.__file__, 'templates')
    app.add_template_dir(path)

​def load(app):
    app.hook.register('post_setup', add_template_dir)

Creating a Plugin Handler

myapp.py
from cement import App
from cement.core.plugin import PluginHandler

class MyPluginHandler(PluginHandler):
    class Meta:
        label = 'my_plugin_handler'

    # do something to implement the interface

class MyApp(App):
    class Meta:
        label = 'myapp'
        plugin_handler = 'my_plugin_handler'
        handlers = [
            MyPluginHandler,
        ]

A list of directory paths where plugin code (modules) can be loaded from (external to the application). Will be merged with and . Default: []

The preferred method of creating a plugin would be via the included :

The example plugin includes a controller, sub-command, and output generated via template. That said, the only thing Cement needs is a load() function.... everything else is arbitrary. In the generated plugin, we find this in myplugin/__init__.py:

All interfaces in Cement can be overridden with your own implementation. This can be done either by sub-classing itself, or by sub-classing an existing extension's handlers in order to alter their functionality.

CementPluginHandler
Plugin
Cement Core Plugin Module
App.Meta
developer tools
Jinja2
PluginHandler
App.Meta.core_system_plugin_dirs
App.Meta.core_user_plugin_dirs