Managing Multiple Environments

Many applications and use-cases call for managing multiple environments and handling common settings between them. These environments might refer to infrastructure such as production, staging, or dev… or perhaps it might be to handle multiple accounts or regions within an account at a service provider.

The following example outlines one possible approach to managing multiple infrastructure accounts from an application Built on Cement:

from import CementApp
from cement.core.controller import CementBaseController, expose
from cement.utils.misc import init_defaults

# set default settings for our different environments
defaults = init_defaults('myapp', 'env.production', 'env.staging', '')
defaults['myapp']['default_env'] = 'production'
defaults['env.production']['foo'] = 'bar.production'
defaults['env.staging']['foo'] = 'bar.staging'
defaults['']['foo'] = ''

# do this in a hook so that we can load the default from config
def set_default_env(app):
    if app.pargs.env is None:
        app.pargs.env = app.config.get('myapp', 'default_env')

class MyController(CementBaseController):
    class Meta:
        label = 'base'
        arguments = [
            (['-E', '--environment'],
             dict(help='environment override',
                  choices=['production', 'staging', 'dev'],

    def default(self):
        print('Inside MyController.default()')

        # shorten things up a bit for clarity
        env_key =
        env ='env.%s' % env_key)

        print('Current Environment: %s' % env_key)
        print('Foo => %s' % env['foo'])

class MyApp(CementApp):
    class Meta:
        label = 'myapp'
        config_defaults = defaults
        base_controller = MyController

with MyApp() as app:
    app.hook.register('post_argument_parsing', set_default_env)


default_env = production

foo = bar.production

foo = bar.staging

foo =

This looks like:

$ python
Inside MyController.default()
Current Environment: production
Foo => bar.production

$ python -E staging
Inside MyController.default()
Current Environment: staging
Foo => bar.staging

$ python -E dev
Inside MyController.default()
Current Environment: dev
Foo =>

The idea being that you can maintain a single set of operations, but modify what or where those operations happen by simply toggling the configuration section (that has the same configuration settings per environment).