Extending CementApp¶

CementApp provides a convenient extend mechanism that allows plugins, extensions, or the app itself to add objects/functions to the global application object. For example, a plugin might extend the CementApp with an api member allowing developers to call app.api.get(...). The application itself does not provide app.api however the plugin does. As plugins are often third party, it is not possible for the plugin developer to simply sub-class the CementApp and add the functionality because the CementApp is already instantiated by the time plugins are loaded.

Take the following for example:

myapp.py

from cement.core.foundation import CementApp

with CementApp('myapp') as app:
    app.run()

The above is a very simple Cement application, which obviously doesn’t do much. That said, we can add a plugin that extends the application to add an API client object, for example, pretty easily. Note the following is an arbitrary and non-functional example using dRest:

/etc/myapp/plugins.d/api.conf

[api]
enable_plugin = true
endpoint = https://example.com/api/v1
user = john.doe
password = XXXXXXXXXXXX

/var/lib/myapp/plugins/api.py

import drest
from cement.core import hook

def extend_api_object(app):
    # get api info from this plugins configuration
    endpoint = app.config.get('api', 'endpoint')
    user = app.config.get('api', 'user')
    password = app.config.get('api', 'password')

    # create an api object and authenticate
    my_api_client = drest.API(endpoint)
    my_api_client.auth(username, password)

    # extend the global app object with an ``api`` member
    app.extend('api', my_api_client)

def load(app):
    hook.register('pre_run', extend_api_object)

In the above plugin, we simply created a dRest API client within a pre_run hook and then extended the global app with it. The developer can now reference app.api anywhere that the global app object is accessible.

Our application code could now look like:

myapp.py

from cement.core.foundation import CementApp

with CementApp('myapp') as app:
    app.run()

    # use the api object that the plugin provides
    app.api.get(...)