config2
Python environment configuration simplified.
Config2 (for Python) - which is highly inspired by node-config
- organizes hierarchical configurations for your app deployments.
It lets you define a set of default parameters, and extend them for different deployment environments (development, qa, staging, production, etc.).
Configurations are stored in configuration files within your application, and can be overridden and extended by environment variables, command line parameters, or external sources.
This gives your application a consistent configuration interface shared among a growing list of npm modules also using node-config.
NOTE: This project is more or less in pair with node-config
implementation, with exception for some fluff that could be considered too much magic such as deployment specific multi-instance deployments which I so far haven't found any good motivation for, and some other questionable advanced features mentioned in the wiki pages.
...based on node-config
project guidelines:
Install using pip:
$ pip install config2
1. Assuming we have a python application project...
some_project
└── app.py
app.py
- some app making serious $$$
# business logic
print('$$$')
2. Let's add some environment specific config files...
some_project
└── config
├── default.yml
├── development.yml
├── foo.yml
└── production.yml
└── app.py
default.yml
- with some bogus nested settings shared for all environments (defaults)
a1: DEFAULT 1
a2:
b1: [1, 2, 3]
b2:
- foo
- bar
b3:
c1: 1
c2: "DEFAULT 2"
development.yml
- with some bogus nested settings overriden for development
environment (overriden)
a2:
b2:
- DEV 1
b3:
c2: "DEV 2"
some_key_only_for_dev: true
foo.yml
- with some bogus nested settings overriden for foo
environment (overriden)
a2:
b2:
- FOO 1
b3:
c2: "FOO 2"
some_key_only_for_foo: true
production.yml
- with some bogus nested settings overriden for production
environment (overriden)
a2:
b2:
- PROD 1
b3:
c2: "PROD 2"
some_key_only_for_prod: true
3. Let's now run the app using various environments...
$ python app.py
from config2.config import config
config.get_env() # => None
config.get() # => {'a1': 'DEFAULT 1', 'a2': {'b1': [1, 2, 3], 'b2': ['foo', 'bar'], 'b3': {'c1': 1, 'c2': 'DEFAULT 2'}}}
config.a1 # => 'DEFAULT 1'
config.a2 # => {'b1': [1, 2, 3], 'b2': ['foo', 'bar'], 'b3': {'c1': 1, 'c2': 'DEFAULT 2'}}
config.a2.b3.c2 # => 'DEFAULT 2'
print('$$$')
$ ENV=development python app.py
from config2.config import config
config.get_env() # => 'development'
config.get() # => {'a1': 'DEFAULT 1', 'a2': {'b1': [1, 2, 3], 'b2': ['DEV 1'], 'b3': {'c1': 1, 'c2': 'DEV 2'}}, 'some_key_only_for_dev': True}
config.a1 # => 'DEFAULT 1'
config.a2 # => {'b1': [1, 2, 3], 'b2': ['DEV 1'], 'b3': {'c1': 1, 'c2': 'DEV 2'}}
config.a2.b3.c2 # => 'DEV was here 2'
config.some_key_only_for_dev # => True
config.some_key_only_for_foo # => AttributeError
config.some_key_only_for_prod # => AttributeError
print('$$$')
$ ENV=foo python app.py
from config2.config import config
config.get_env() # => 'foo'
config.get() # => {'a1': 'DEFAULT 1', 'a2': {'b1': [1, 2, 3], 'b2': ['FOO 1'], 'b3': {'c1': 1, 'c2': 'FOO 2'}}, 'some_key_only_for_foo': True}
config.a1 # => 'DEFAULT 1'
config.a2 # => {'b1': [1, 2, 3], 'b2': ['FOO 1'], 'b3': {'c1': 1, 'c2': 'FOO 2'}}
config.a2.b3.c2 # => 'FOO was here 2'
config.key_only_for_foo # => True
config.some_key_only_for_dev # => AttributeError
config.some_key_only_for_prod # => AttributeError
print('$$$')
$ ENV=production python app.py
from config2.config import config
config.get_env() # => 'production'
config.get() # => {'a1': 'DEFAULT 1', 'a2': {'b1': [1, 2, 3], 'b2': ['PROD 1'], 'b3': {'c1': 1, 'c2': 'PROD 2'}}, 'some_key_only_for_foo': True}
config.a1 # => 'DEFAULT 1'
config.a2 # => {'b1': [1, 2, 3], 'b2': ['PROD 1'], 'b3': {'c1': 1, 'c2': 'PROD 2'}}
config.a2.b3.c2 # => 'PROD was here 2'
config.some_key_only_for_prod # => True
config.some_key_only_for_dev # => AttributeError
config.some_key_only_for_foo # => AttributeError
print('$$$')
etc.
4. Optionally, let's now introduce custom config environment variables...
some_project
└── config
├── custom-environment-variables.yml
├── default.yml
├── development.yml
├── foo.yml
└── production.yml
└── app.py
custom-environment-variables.yml
- with mappings of config keys to environment variables
a1: A1
a2:
b3:
c2: C2
5. Let's now run the app using custom environment variables to override config...
$ A1=x C2=y python app.py
from config2.config import config
config.get_env() # => None
config.get() # => {'a1': 'x', 'a2': {'b1': [1, 2, 3], 'b2': ['foo', 'bar'], 'b3': {'c1': 1, 'c2': 'y'}}}
config.a1 # => 'x'
config.a2 # => {'b1': [1, 2, 3], 'b2': ['foo', 'bar'], 'b3': {'c1': 1, 'c2': 'y'}}
config.a2.b3.c2 # => 'y'
print('$$$')
Clone down source code:
$ make install
Run colorful tests, with only native environment (dependency sandboxing up to you):
$ make test
Run less colorful tests, with multi-environment (using tox):
$ make test-tox
This project was mainly initiated - in lack of existing alternatives - to be used at our work at Markable.ai to have common code conventions between various programming environments where Python (research, CV, AI) and Node.js (I/O, APIs, UIs, scripts) currently are most used.
Released under the MIT license.