config-argparse

config library based on standard ArgumentParser


Keywords
argparse, config, python3
License
MIT
Install
pip install config-argparse==0.3.3

Documentation

config_argparse

Config library based on standard ArgumentParser.

Usage

Simple:

from config_argparse import Config

class MyConfig(Config):
    weight = 10.0         # -> .add_argument('--weight', type=float, default=10.0)
    layers = [10, 20, 30] # -> .add_argument('--layers', type=int, nargs='+', default=[10, 20, 30])

MyConfig()
# MyConfig:
#         weight = 10.0
#         layers = [10, 20, 30]

MyConfig().parse_args(['--weight', '30.5', '--layers', '1', '2'])
# MyConfig:
#        weight = 30.5
#        layers = [1, 2]

MyConfig().parse_args(['--help'])
# usage: MyConfig [-h] [--weight WEIGHT] [--layers LAYERS [LAYERS ...]]
#
# optional arguments:
#   -h, --help            show this help message and exit
#   --weight WEIGHT
#   --layers LAYERS [LAYERS ...]

You can use int, float, str, bool, and list of them.

config_argparse.Config automatically generates argparse.ArgumentParser internally and parse arguments.

All the values defined as class variables are copied to the instance by copy.deepcopy.

Nested:

This library supports nested config (it generates multiple ArgumentParser internally).

from config_argparse import Config

class Nest(Config):
    a = 10

class MyConfig(Config):
    a = 'str'
    nest = Nest()  # -> .add_argument('--nest.a', type=int, default=10, dest=`a in nest`)

MyConfig()
# MyConfig:
#         a = str
#         nest = Nest:
#                 a = 10

MyConfig().parse_args(['--nest.a', '33'])
# MyConfig:
#        a = str
#        nest = Nest:
#                a = 33

MyConfig().parse_args(['--help'])
# usage: MyConfig [-h] [--a A] [--nest]
#
# optional arguments:
#   -h, --help  show this help message and exit
#   --a A
#   --nest

MyConfig().parse_args(['--nest.help'])
# usage: Nest [-h] [--nest.a A]
#
# optional arguments:
#   -h, --help  show this help message and exit
#   --nest.a A

Inherited:

You can merge multiple configs by inheriting them.

from config_argparse import Config

class ParentA(Config):
    a = 10

class ParentB(Config):
    b = 10

class MyConfig(ParentA, ParentB):
    c = 'str'

MyConfig()
# MyConfig:
#         a = 10
#         b = 10
#         c = str

MyConfig().parse_args(['--a', '33'])
# MyConfig:
#         a = 33
#         b = 10
#         c = str

Dynamic:

You can dynamically build nested configs according to the values in the parent.

from config_argparse import Config, Value, DynamicConfig

class ConfigA(Config):
    a = 1

class ConfigB(Config):
    b = 1

config_map = {
    'a': ConfigA,
    'b': ConfigB,
}

class MyConfig(Config):
    lr = 0.1
    epoch = 100
    model = Value('a', choices=list(config_map.keys()))
    model_cfg = DynamicConfig(lambda c: config_map[c.model]()) # pass function which returns instance of Config

args = MyConfig()
print(args)
# MyConfig:
#         lr = 0.1
#         epoch = 100
#         model = a
#         model_cfg = None # default value of DynamicConfig is None (if auto_load=False)

args = MyConfig().parse_args([])
print(args)
# MyConfig:
#         lr = 0.1
#         epoch = 100
#         model = a
#         model_cfg = ConfigA:
#               a = 1

args = MyConfig().parse_args(['--lr', '1', '--model', 'b', '--model_cfg.b', '100'])
print(args)
# MyConfig:
#         lr = 1.0
#         epoch = 100
#         model = b
#         model_cfg = ConfigB:
#                 b = 100

DynamicConfig also supports list of Configs.

class ConfigA(Config):
    a = 1

class MyConfig(Config):
    models = ['a', 'b', 'c']
    # pass function which returns list of Config
    models_cfg = DynamicConfig(lambda c: list(map(lambda x: ConfigA(), c.models)))

args = MyConfig()
print(args)
# MyConfig:
#         models = ['a', 'b', 'c']
#         models_cfg = None
print(args.parse_args([]))
# MyConfig:
#         models = ['a', 'b', 'c']
#         models_cfg = [ConfigA:
#                 a = 1, ConfigA:
#                 a = 1, ConfigA:
#                 a = 1]

Value:

You can use some ArgumentParser's features such as required, choices by config_argparse.Value.

from config_argparse import Config, Value

class MyConfig(Config):
    weight = Value(type=float, required=True)
    dim = Value('a', choices=['a', 'b', 'c'])

MyConfig().parse_args([])
# MyConfig: error: the following arguments are required: --weight

MyConfig().parse_args(['--weight', '1.0', '--dim', 'foo'])
# MyConfig: error: argument --dim: invalid choice: 'foo' (choose from 'a', 'b', 'c')

Convert to/from dict

Default values can be set by passing dict like object to Config::__init__.

Config::todict creates serializable dict (if the config is already parsed).

import json
from config_argparse import Config

class MyConfig(Config):
    weight = 10.0         # -> .add_argument('--weight', type=float, default=10.0)
    layers = [10, 20, 30] # -> .add_argument('--layers', type=int, nargs='+', default=[10, 20, 30])

c = MyConfig({'weight': 1.0})
c.weight
# -> 1.0
c.todict()
# -> {'weight': 1.0, 'layers': [10, 20, 30]}