Configuration¶
Bottle applications can store their configuration in Bottle.config
, a dict-like object and central place for application specific settings. This dictionary controls many aspects of the framework, tells (newer) plugins what to do, and can be used to store your own configuration as well.
Configuration Basics¶
The Bottle.config
object behaves a lot like an ordinary dictionary. All the common dict methods work as expected. Let us start with some examples:
import bottle
app = bottle.default_app() # or bottle.Bottle() if you prefer
app.config['autojson'] = False # Turns off the "autojson" feature
app.config['sqlite.db'] = ':memory:' # Tells the sqlite plugin which db to use
app.config['myapp.param'] = 'value' # Example for a custom config value.
# Change many values at once
app.config.update({
'autojson': False,
'sqlite.db': ':memory:',
'myapp.param': 'value'
})
# Add default values
app.config.setdefault('myapp.param2', 'some default')
# Receive values
param = app.config['myapp.param']
param2 = app.config.get('myapp.param2', 'fallback value')
# An example route using configuration values
@app.route('/about', view='about.rst')
def about():
email = app.config.get('my.email', 'nomail@example.com')
return {'email': email}
Helper functions can rely on Request.app
to get the application
and configuration associated with the current request:
from bottle import request
def is_admin(user):
return user == request.app.config['myapp.admin_user']
Naming Convention¶
To make life easier, plugins and applications should follow some simple rules when it comes to config parameter names:
All keys should be lowercase strings and follow the rules for python identifiers (no special characters but the underscore).
Namespaces are separated by dots (e.g.
namespace.field
ornamespace.subnamespace.field
).Bottle uses the root namespace for its own configuration. Plugins should store all their variables in their own namespace (e.g.
sqlite.db
orwerkzeug.use_debugger
).Your own application should use a separate namespace (e.g.
myapp.*
).
Load configuration from a File¶
Configuration files are useful if you want to enable non-programmers to configure your application, or just don’t want to hack python module files just to change the database port. A very common syntax for configuration files is shown here:
[sqlite]
db = /tmp/test.db
commit = auto
[myapp]
admin_user = defnull
With ConfigDict.load_config()
you can load these *.ini
style configuration
files from disk and import their values into your existing configuration:
app.config.load_config('/etc/myapp.conf')
Load configuration from a python module¶
Loading configuration from a Python module is a common pattern for Python
programs and frameworks. ConfigDict.load_module()
imports a python
module by name and adds all upper-case variables to configuration.
DEBUG = True
SQLITE = {"db": ":memory:"}
>>> c = ConfigDict()
>>> c.load_module('config')
{'debug': True, 'sqlite.db': 'memory'}
>>> c.load_module("config", squash=False)
{'debug': True, 'sqlite': {'db': 'memory'}}
Configuration is flattened by default, similar to the behavior of ConfigDict.load_dict()
. You can prevent that by passing squash=False
as a parameter.
Loading configuration from a dict
¶
Another useful method is ConfigDict.load_dict()
. This method takes
an entire structure of nested dictionaries and turns it into a flat list
of keys and values with namespaced keys:
# Load an entire dict structure
app.config.load_dict({
'autojson': False,
'sqlite': { 'db': ':memory:' },
'myapp': {
'param': 'value',
'param2': 'value2'
}
})
assert app.config['myapp.param'] == 'value'
# Load configuration from a json file
with open('/etc/myapp.json') as fp:
app.config.load_dict(json.load(fp))
Listening to configuration changes¶
Bottle triggers the config
application hook each time a value in Bottle.config
is about to be changed.
This hook can be used to dynamically re-configure plugins or parts of your application once configuration changes at runtime.
For example:
Enable or disable maintenance or debug mode.
Reconnect to a new database.
Change the settings on a plugin or background service.
Resize worker thread pools.
The hook callback receives two arguments (key, new_value) and is called before the value is actually changed in the dictionary. Raising an exception from this callback will prevent the change and preserve the old value.
@app.hook('config')
def on_config_change(key, value):
if key == 'debug':
switch_own_debug_mode_to(value)
The hook callbacks cannot change the value that is to be stored to the dictionary. That is what filters are for.
Filters and other Meta Data¶
ConfigDict
allows you to store meta data along with configuration keys. Two meta fields are currently defined:
help
A help or description string. May be used by debugging, introspection or admin tools to help the site maintainer configuring their application.
filter
A callable that accepts and returns a single value. If a filter is defined for a key, any new value stored to that key is first passed through the filter callback. The filter can be used to cast the value to a different type, check for invalid values (throw a ValueError) or trigger side effects.
This feature is most useful for plugins. They can validate their config parameters or trigger side effects using filters and document their configuration via help
fields:
class SomePlugin(object):
def setup(app):
app.config.meta_set('some.int', 'filter', int)
app.config.meta_set('some.list', 'filter',
lambda val: str(val).split(';'))
app.config.meta_set('some.list', 'help',
'A semicolon separated list.')
def apply(self, callback, route):
...
import bottle
app = bottle.default_app()
app.install(SomePlugin())
app.config['some.list'] = 'a;b;c' # Actually stores ['a', 'b', 'c']
app.config['some.int'] = 'not an int' # raises ValueError
API Documentation¶
- class ConfigDict[source]
A dict-like configuration storage with additional support for namespaces, validators, meta-data and overlays.
This dict-like class is heavily optimized for read access. Read-only methods and item access should be as fast as a native dict.
- __init__()[source]
- load_module(name, squash=True)[source]
Load values from a Python module.
Import a python module by name and add all upper-case module-level variables to this config dict.
- Parameters:
name – Module name to import and load.
squash – If true (default), nested dicts are assumed to represent namespaces and flattened (see
load_dict()
).
- load_config(filename, **options)[source]
Load values from
*.ini
style config files using configparser.INI style sections (e.g.
[section]
) are used as namespace for all keys within that section. Both section and key names may contain dots as namespace separators and are converted to lower-case.The special sections
[bottle]
and[ROOT]
refer to the root namespace and the[DEFAULT]
section defines default values for all other sections.- Parameters:
filename – The path of a config file, or a list of paths.
options – All keyword parameters are passed to the underlying
configparser.ConfigParser
constructor call.
- load_dict(source, namespace='')[source]
Load values from a dictionary structure. Nesting can be used to represent namespaces.
>>> c = ConfigDict() >>> c.load_dict({'some': {'namespace': {'key': 'value'} } }) {'some.namespace.key': 'value'}
- update(*a, **ka)[source]
If the first parameter is a string, all keys are prefixed with this namespace. Apart from that it works just as the usual dict.update().
>>> c = ConfigDict() >>> c.update('some.namespace', key='value')
- setdefault(key, value=None)[source]
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- meta_get(key, metafield, default=None)[source]
Return the value of a meta field for a key.
- meta_set(key, metafield, value)[source]
Set the meta field for a key to a new value.
Meta-fields are shared between all members of an overlay tree.
- meta_list(key)[source]
Return an iterable of meta field names defined for a key.