dynaconf - The dynamic configurator for your Python Project
dynaconf a layered configuration system for Python applications -
with strong support for 12-factor applications
and Flask app.config
extension.
# install it in a Python 3 environment
pip3 install dynaconf
# optionally
pip3 install PyYAML toml redis
# import the unique dynaconf object
from dynaconf import settings
# access your config variables
Connect(user=settings.USERNAME, passwd=settings.PASSWD)
# You can provide defaults in case config is missing
Connect(user=settings('USERNAME', 'admin'), passwd=settings('PASSWD', 1234))
Dynaconf will look for variables in the following order (by default) and you can also customize the order of loaders.
- Settings files files in the order:
settings.{py|yaml|toml|ini|json}
.env
fileexport
ed Environment Variables- Remote storage servers
- Multiple customizable sources
# put some variable in a .env file
echo "DYNACONF_USERNAME=admin" >> .env
# Or export directly
export DYNACONF_USERNAME=admin
export DYNACONF_PASSWD='@int 1234' # you can type the values!
Just read it
# import the unique dynaconf object
from dynaconf import settings
# access your config variables
Connect(user=settings.USERNAME, passwd=settings.PASSWD)
NOTE: You can customize the prefix
DYNACONF_
to your own namespace likeMYAPP_USERNAME
.
- Read config variables from unique
dynaconf
object - Values can come from different sources:
- Load values from
settings.{py|yaml|toml|ini}
file - Load values from
.env
files - Load values from System's Exported
Environment Variables
- And even more:
- Load values from a remote Redis server
- Load values from a remote SQL database
- Load values from a remote memcached
- Load values from a remote Secrets Vault
- And if you want:
- Load values from anywhere you want, easy to implement your own loader!
- Load values from
- Flexible typing system
- Export Typed environment variables using dynaconf data type markers
export FOO=@int 42
- Export Typed environment variables using dynaconf data type markers
- Flask Support
- In your Flask application just do
FlaskDynaconf(app)
and then read values fromapp.config
object
- In your Flask application just do
- Feature Flag System
- Implement a feature flag system using dynaconf as store and checker
- Value validation
- Validate the config variables and define rules
When you are working with multiple projects using the same environment maybe you want to use different namespaces for ENV vars based configs
export DYNACONF_DATABASE="DYNADB"
export PROJ1_DATABASE="PROJ1DB"
export PROJ2_DATABASE="PROJ2DB"
and then access them
>>> from dynaconf import settings
# access default namespace settings
>>> settings.DATABASE
'DYNADB'
# switch namespaces
>>> settings.namespace('PROJ1')
>>> settings.DATABASE
'PROJ1DB'
>>> settings.namespace('PROJ2')
>>> settings.DATABASE
'PROJ2DB'
# return to default, call it without args
>>> settings.namespace()
>>> settings.DATABASE
'DYNADB'
You can also use the context manager:
>>> settings.DATABASE
'DYNADB'
>>> with settings.using_namespace('PROJ1'):
... settings.DATABASE
'PROJ1DB'
>>> with settings.using_namespace('PROJ2'):
... settings.DATABASE
'PROJ2DB'
>>> settings.DATABASE
'DYNADB'
namespace() and using_namespace() takes optional argument clean defaults to True. If you want to keep the pre-loaded values when switching namespaces set it to False.
It is usual to have e.g production
and development
environments, the way to set this is:
settings.py
development_settings.py
production_settings.py
Then in your environment.
export DYNACONF_NAMESPACE=DEVELOPMENT|PRODUCTION # switch enviroment using env vars.
Or using namespace
with settings.using_namespace('development'):
# code here
settings.namespace('development')
NOTE:
settings.py
is the base andnamespace
specific overrides its vars.
you need to install PyYAML
pip install PyYAML
Just save a settings.yaml
in the root dir.
Using YAML is easier because it support multiple namespace in a single file.
Lets say you have DYNACONF_NAMESPACE=DYNACONF
(the default)
DYNACONF: # this is the global namespace
VARIABLE: 'this variable is available on every namespace'
HOST: null # this variable is set to None
DEVELOPMENT:
HOST: devserver.com # overrides the global or sets new
production: # upper or lower case does not matter
host: prodserver.com
Then it will be applied using env var DYNACONF_NAMESPACE
or context manager.
HINT: When using yaml namespace identifier and first level vars are case insensitive, dynaconf will always have them read as upper case.
you need to install toml
pip install toml
Just save a settings.toml
in the root dir.
Using TOML is easier because it support multiple namespace in a single file.
Lets say you have DYNACONF_NAMESPACE=DYNACONF
(the default)
[dynaconf]: # this is the global namespace
variable = 'this variable is available on every namespace'
HOST = false # this variable is set to False
[DEVELOPMENT]
HOST = 'devserver.com' # overrides the global or sets new
[production] # upper or lower case does not matter
host = 'prodserver.com'
Then it will be applied using env var DYNACONF_NAMESPACE
or context manager.
HINT: When using toml namespace identifier and first level vars are case insensitive, dynaconf will always have them read as upper case.
you need to install configobj
pip install configobj
Just save a settings.ini
in the root dir.
Using INI is easier because it support multiple namespace in a single file.
Lets say you have DYNACONF_NAMESPACE=DYNACONF
(the default)
[DYNACONF]
VARIABLE = "this variable is available on every namespace"
[DEVELOPMENT]
HOST = "devserver.com"
[production]
host = "prodserver.com"
Then it will be applied using env var DYNACONF_NAMESPACE
or context manager.
HINT: When using INI namespace identifier and first level vars are case insensitive, dynaconf will always have them read as upper case.
Just save a settings.json
in the root dir.
Using JSON is easier because it support multiple namespace in a single file.
Lets say you have DYNACONF_NAMESPACE=DYNACONF
(the default)
{
"DYNACONF": {
"VARIABLE": "this variable is available on every namespace",
"HOST": null
},
"DEVELOPMENT": {
"HOST": "devserver.com"
},
"production": {
"host": "prodserver.com"
}
}
Then it will be applied using env var DYNACONF_NAMESPACE
or context manager.
HINT: When using json namespace identifier and first level vars are case insensitive, dynaconf will always have them read as upper case.
Sometimes you need to set some values as specific types, boolean, integer, float or lists and dicts.
built in casts
- @int (as_int)
- @bool (as_bool)
- @float (as_float)
- @json (as_json)
@json / as_json will use json to load a Python object from string, it is useful to get lists and dictionaries. The return is always a Python object.
strings does not need converters.
You have 2 ways to use the casts.
Just start your ENV settigs with this
export DYNACONF_DEFAULT_THEME='material'
export DYNACONF_DEBUG='@bool True'
export DYNACONF_DEBUG_TOOLBAR_ENABLED='@bool False'
export DYNACONF_PAGINATION_PER_PAGE='@int 20'
export DYNACONF_MONGODB_SETTINGS='@json {"DB": "quokka_db"}'
export DYNACONF_ALLOWED_EXTENSIONS='@json ["jpg", "png"]'
Starting the settings values with @ will make dynaconf.settings to cast it in the time od load.
export DYNACONF_USE_SSH='yes'
from dynaconf import settings
use_ssh = settings.get('USE_SSH', cast='@bool')
# or
use_ssh = settings('USE_SSH', cast='@bool')
# or
use_ssh = settings.as_bool('USE_SSH')
print use_ssh
True
export DYNACONF_USE_SSH='enabled'
export DYNACONF_ALIST='@json [1,2,3]'
export DYNACONF_ADICT='@json {"name": "Bruno"}'
export DYNACONF_AINT='@int 42'
export DYNACONF_ABOOL='@bool on'
export DYNACONF_AFLOAT='@float 42.5'
from dynaconf import settings
# original value
settings('USE_SSH')
'enabled'
# cast as bool
settings('USE_SSH', cast='@bool')
True
# also cast as bool
settings.as_bool('USE_SSH')
True
# cast defined in declaration '@bool on'
settings.ABOOL
True
# cast defined in declaration '@json {"name": "Bruno"}'
settings.ADICT
{u'name': u'Bruno'}
# cast defined in declaration '@json [1,2,3]'
settings.ALIST
[1, 2, 3]
# cast defined in decalration '@float 42.5'
settings.AFLOAT
42.5
# cast defined in declaration '@int 42'
settings.AINT
42
Include in the file defined in DYNACONF_SETTINGS or in the .env
file or in the customized Settings class the desired namespace
DYNACONF_NAMESPACE = 'DYNACONF'
Redis support relies on the following two settings that you can setup in the DYNACONF_SETTINGS file
1 Add the configuration for redis client
REDIS_FOR_DYNACONF = {
'host': 'localhost',
'port': 6379,
'db': 0
}
NOTE: if running on Python 3 include
'decode_responses': True
inREDIS_FOR_DYNACONF
Include redis_loader in dynaconf LOADERS_FOR_DYNACONF
the order is the precedence
# Loaders to read namespace based vars from diferent data stores
LOADERS_FOR_DYNACONF = [
'dynaconf.loaders.env_loader',
'dynaconf.loaders.redis_loader'
]
You can now write variables direct in to a redis hash named DYNACONF_< NAMESPACE >
By default DYNACONF_DYNACONF
You can also use the redis writer
from dynaconf.utils import redis_writer
from dynaconf import settings
redis_writer.write(settings, name='Bruno', database='localhost', PORT=1234)
The above data will be converted to namespaced values and recorded in redis as a hash:
DYNACONF_DYNACONF:
NAME='Bruno'
DATABASE='localhost'
PORT='@int 1234'
if you want to skip type casting, write as string intead of PORT=1234 use PORT='1234' as redis stores everything as string anyway
Data is read from redis and another loaders only once or when namespace() and using_namespace() are invoked. You can access the fresh value using settings.get_fresh(key)
There is also the fresh context manager
from dynaconf import settings
print settings.FOO # this data was loaded once on import
with settings.fresh():
print settings.FOO # this data is being directly read from loaders
And you can also force some variables to be fresh setting in your setting file
DYNACONF_ALWAYS_FRESH_VARS = ['MYSQL_HOST']
or using env vars
export DYNACONF_ALWAYS_FRESH_VARS='@json ["MYSQL_HOST"]'
Then
from dynaconf import settings
print settings.FOO # This data was loaded once on import
print settings.MYSQL_HOST # This data is being read from redis imediatelly!
Sometimes you want to override settings for your existing Package or Framework lets say you have a conf module exposing a settings object and used to do:
from myprogram.conf import settings
Now you want to use Dynaconf, open that conf.py
or conf/__init__.py
and do:
# coding: utf-8
from dynaconf import LazySettings
settings = LazySettings(
ENVVAR_FOR_DYNACONF="MYPROGRAM_SETTINGS_MODULE",
DYNACONF_NAMESPACE='MYPROGRAM'
)
Now you can import settings from your own program and dynaconf will do the rest!
Dynaconf provides an extension to make your app.config
in Flask to be a dynaconf
instance.
from flask import Flask
from dynaconf import FlaskDynaconf
app = Flask(__name__)
FlaskDynaconf(app)
The FlaskDynaconf
takes optional arguments
ENVVAR_FOR_DYNACONF="MYSITE_SETTINGS_MODULE" # defaults to `DYNACONF_SETTINGS`
DYNACONF_NAMESPACE='MYSITE' # defaults to `FLASK_`
SETTINGS_MODULE_FOR_DYNACONF='settings.yml' # defaults to `settings.py`
YAML='.secrets.yml', # aditional config
EXTRA_VALUE='You can add aditional config vars here'
By default Dynaconf only outputs the ERROR level to debug it change
export DYNACONF_DEBUG_LEVEL='INFO'
Dynaconf will perform loads in this order:
- Load Default configuration
- Load Environment variables (pre load to read initial DYNACONF_ config)
- Load Settings file in the order defined in
SETTINGS_MODULE_FOR_DYNACONF
by default will to load'settings.py,settings.yaml,settings.toml'
in this order overriding previous values - Load all loaders defined in
LOADERS_FOR_DYNACONF
by default onlyenvironment variables
will be read again and get higher precedence
In a setting file like settings.{py|yaml|toml}
define:
LOADERS_FOR_DYNACONF = [
'dynaconf.loaders.env_loader',
'dynaconf.loaders.redis_loader',
'YourCustomLoaderPath'
]
export also works
export LOADERS_FOR_DYNACONF='@json ["loader1", "loader2"]'
Loaders will be executed in that order.
To disable loaders do:
LOADERS_FOR_DYNACONF=0
This will cause environment variables to lose the higher precedence
Take a look at example/ for more.
This was inspired by flask.config and django.conf.settings