Set custom configurations on a system level that your application can use

Author:Peter Bengtsson

Certain properties are best stored persistently inside your application. Other properties are more appropriate to store on a “system” level.

Purpose

You want to set a configuration variable that affects how your Grok application is running. Perhaps you have two instances running the same application on different servers. One which should log all emails sent through your application since you’re doing some statistics or debugging and one where you want to leave it as is.

One alternative is to set it as a persistent property inside the application itself as part of the ZODB but that would mean changes to the functionality or perhaps you feel that the configurations you want to set are a sys admins problem and not the application manager.

Another alternative is to set environment variables in your system and then use os.environ to pick up the values but this is generally a bad idea since environment variables are difficult to “connect” to the application and it’s really hard to tell if them have been set before your application starts.

Step by step

First of all, the key is to use the file zope.conf in <zopeinstance>/parts/zopectl/ but this file is autogenerated by buildout which means that if you make any changes to it (in fact, any changes inside parts/*) you’re likely to loose them the next time your run buildout.

The ticket is to change the blueprint of your project: buildout.cfg. By default it probably looks something like this:

[zopectl]
recipe = zc.zope3recipes:instance
application = app
zope.conf = ${data:zconfig}

This is your chance to add your own lines to go into zope.conf which goes on the next line like this:

[zopectl]
recipe = zc.zope3recipes:instance
application = app
zope.conf = ${data:zconfig}
            <product-config name_of_product_or_app>
              debug-email-sending 1
              dump-directory ${buildout:directory}/parts/dumps
            </product-config>

It might seem a little strange at first but is incredibly powerful and works well. Run buildout again and check for yourself that those extra lines were included by buildout by taking a look at parts/zopectl/zope.conf.

Now you can reach these inside your application and use as you please:

from zope.app.appsetup.product import getProductConfiguration
# see parts/zopectl/zope.conf
config = getProductConfiguration('name_of_product_or_app')

...
DEBUG_EMAIL_SENDING = config.get('debug-email-sending', False)
DUMP_DIRECTORY = config.get('dump-directory', '/tmp/dumps')

The function getProductConfiguration() will return None if there is no configuration under that name which means that you an AttributeError if you call .get() on it. So, if you envision that you might not set a configuration at all you should probably add these two lines so it looks like this:

config = getProductConfiguration('name_of_product_or_app')
if config is None:
    config = {} # config.get() below will return the default values

Further information

At the time of writing this I can’t find any documentation other than the Zope 3 API doc which is rather sparse. However, the syntax couldn’t be easier to use. It’s just a matter of remembering that this great tool is there.

EDIT

This how-to was rewritten after first publish by help of Philipp von Weitershausen. Thanks Philipp!