Skip to main content

Globally scoped configuration with argparse integration

Project description

Effortless Config

PyPI version CircleCI

Globally scoped configuration with argparse integration.

Installation

pip install effortless-config

Rationale

  1. When building machine learning models, I often find myself with a file named config.py that has a bunch of global variables that I reference throughout the codebase.
  2. As the work progresses, I end up with groups of specific configuration settings that correspond to specific experiments.
  3. I want to be able to select configuration group on the command line when I start an experiment.
  4. And I want to be able to override certain settings within that experiment, from the command line.

Basic usage

First, define a class that extends effortless_config.Config and specify your default configuration values inside it:

from effortless_config import Config

class config(Config):  # lowercase "c" optional

    SOME_INTEGER_SETTING = 10
    FLOAT_SETTING = 0.5
    A_BOOLEAN = False
    MY_STRING_SETTING = 'foo'

Then you can refer to these settings elsewhere in your code base:

def main():
    print(f'SOME_INTEGER_SETTING is {config.SOME_INTEGER_SETTING}')
    print(f'FLOAT_SETTING is {config.FLOAT_SETTING}')
    print(f'A_BOOLEAN is {config.A_BOOLEAN}')
    print(f'MY_STRING_SETTING is {config.MY_STRING_SETTING}')

The function config.parse_args() creates an argparse parser from your configuration. If we put the two previous code snippets in a file called basic_example.py and finish it off with

if __name__ == '__main__':
    config.parse_args()
    main()

...we can see the available settings with -h/--help:

$ python basic_example.py --help
usage: basic_example.py [-h] [--some-integer-setting SOME_INTEGER_SETTING]
                        [--float-setting FLOAT_SETTING] [--a-boolean {true,false}]
                        [--my-string-setting MY_STRING_SETTING]

optional arguments:
  -h, --help            show this help message and exit
  --some-integer-setting SOME_INTEGER_SETTING
  --float-setting FLOAT_SETTING
  --a-boolean {true,false}
  --my-string-setting MY_STRING_SETTING

We can then override configuration settings from the command line:

$ python basic_example.py
SOME_INTEGER_SETTING is 10
FLOAT_SETTING is 0.5
A_BOOLEAN is False
MY_STRING_SETTING is foo

$ python basic_example.py --some-integer-setting 1000
SOME_INTEGER_SETTING is 1000
FLOAT_SETTING is 0.5
A_BOOLEAN is False
MY_STRING_SETTING is foo

List and dict type settings can be overridden by passing json strings from the command line, e.g.

$ python lists_and_dicts.py --my-list='[1, 2, 3]' --my-dict='{"foo": 10, "bar": 20}'

Using groups

Configuration groups are families of settings that you want to tie together. For example, you might have some machine learning problem for which you have a large model and a small model, and you want to easily switch between the two models without having to specify all parameters on the command line every time.

To specify groups, use the settings(...) function:

from effortless_config import Config, setting

class config(Config):
    groups = ['large', 'small']

    NUM_LAYERS = setting(default=5, large=10, small=3)
    NUM_UNITS = setting(default=128, large=512, small=32)
    USE_SKIP_CONNECTIONS = setting(default=True, small=False)
    LEARNING_RATE = 0.1
    OPTIMIZER = 'adam'

setting(...) has the signature:

def setting(default: T, **kwargs: T) -> T

...where T is Union[int, float, str, bool, list, dict, NoneType] and kwargs is a map from group names to values. Specifying parameters by value is shorthand for a setting with no groups, i.e. SOME_KEY = 'value' is equivalent to SOME_KEY = setting(default='value').

When using groups you must first define the group names using the config.groups list.

Then in your code you can use these settings like in the basic example

def main():
    print(f'NUM_LAYERS is {config.NUM_LAYERS}')
    print(f'NUM_UNITS is {config.NUM_UNITS}')
    print(f'USE_SKIP_CONNECTIONS is {config.USE_SKIP_CONNECTIONS}')
    print(f'LEARNING_RATE is {config.LEARNING_RATE}')
    print(f'OPTIMIZER is {config.OPTIMIZER}')


if __name__ == '__main__':
    config.parse_args()
    main()

Now we see an additional --configuration option when we ask for --help:

$ python group_example.py --help
usage: group_example.py [-h] [--configuration {default,large,small}]
                        [--num-layers NUM_LAYERS] [--num-units NUM_UNITS]
                        [--use-skip-connections {true,false}]
                        [--learning-rate LEARNING_RATE] [--optimizer OPTIMIZER]

optional arguments:
  -h, --help            show this help message and exit
  --configuration {default,large,small}, -c {default,large,small}
  --num-layers NUM_LAYERS
  --num-units NUM_UNITS
  --use-skip-connections {true,false}
  --learning-rate LEARNING_RATE
  --optimizer OPTIMIZER

The --configuration option specifies the configuration group, and defaults to default if omitted.

For example:

$ python group_example.py
NUM_LAYERS is 5
NUM_UNITS is 128
USE_SKIP_CONNECTIONS is True
LEARNING_RATE is 0.1
OPTIMIZER is adam

$ python group_example.py --configuration large
NUM_LAYERS is 10
NUM_UNITS is 512
USE_SKIP_CONNECTIONS is True
LEARNING_RATE is 0.1
OPTIMIZER is adam

$ python group_example.py --configuration small
NUM_LAYERS is 3
NUM_UNITS is 32
USE_SKIP_CONNECTIONS is False
LEARNING_RATE is 0.1
OPTIMIZER is adam

We can also override individual settings in conjunction with groups. Individual settings take precedence over the group setting:

$ python group_example.py --configuration large --num-units 768
NUM_LAYERS is 10
NUM_UNITS is 768
USE_SKIP_CONNECTIONS is True
LEARNING_RATE is 0.1
OPTIMIZER is adam

Testing

When writing tests, you can use the config.override context manager to override individual settings:

import pytest
from .config import config

def test_with_context_manager():
    with config.override(FLOAT_SETTING=0.8, A_BOOLEAN=True):
        assert config.FLOAT_SETTING * config.SOME_INTEGER_SETTING == 8
        assert config.A_BOOLEAN is True

The config.override method can also be used without context management in conjunction with config.reset_to_defaults:

def test_with_manual_reset():
    config.override(FLOAT_SETTING=0.8, A_BOOLEAN=True)
    assert config.FLOAT_SETTING * config.SOME_INTEGER_SETTING == 8
    assert config.A_BOOLEAN is True
    config.reset_to_defaults()

Btw, this has nothing to do with Chef's Effortless Config project, I forgot to google the name before I put it on pypi and now I guess I'm stuck with it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

effortless-config-0.7.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

effortless_config-0.7.0-py3-none-any.whl (8.0 kB view details)

Uploaded Python 3

File details

Details for the file effortless-config-0.7.0.tar.gz.

File metadata

  • Download URL: effortless-config-0.7.0.tar.gz
  • Upload date:
  • Size: 7.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/49.2.0 requests-toolbelt/0.8.0 tqdm/4.37.0 CPython/3.7.4

File hashes

Hashes for effortless-config-0.7.0.tar.gz
Algorithm Hash digest
SHA256 b6349ad0acfafffbe19615b8581924eaef6e8cdc65c6b5e3c14eb561c65c4dcb
MD5 58c1b59a2e087e2c8e0e20b3c8bb3328
BLAKE2b-256 4c1f9b9adff7b87adbf695e86bbfc9cd6d7582a889deb8a9c52fbe80d89928ef

See more details on using hashes here.

File details

Details for the file effortless_config-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: effortless_config-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 8.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/49.2.0 requests-toolbelt/0.8.0 tqdm/4.37.0 CPython/3.7.4

File hashes

Hashes for effortless_config-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c2d872122e736bf95a050942cb51c5cf3ffdbc96120579874f0268d5acc573d
MD5 6fda168b3beac9617c833d39847e3ebe
BLAKE2b-256 8a628c2ee88ada360fc9d572df37863181d4d448dcdffb7f44759faf70ae30e0

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page