grimen / python-config2

Python environment configuration simplified - highly inspired by `node-config`.
https://pypi.org/project/config2
MIT License
20 stars 9 forks source link
application config configuration configurations env environment environments node-config python python-config settings

config2 PyPI version Build Status Coverage Status

Python environment configuration simplified.

Introduction

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.

Project Guidelines

...based on node-config project guidelines:

Install

Install using pip:

$ pip install config2

Use

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('$$$')

Test

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

About

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.

License

Released under the MIT license.