Download: https://osdyn.ifremer.fr/pyweb/notebooks/utils/cfmg/configmanager_usage.ipynb

misc.config.ConfigManager

which allows to use some parameters stored in configuration files in a much more flexible way than what the standard module ConfigParser provides.

Create cfg files

cfgini = """
[scalars]
    boolean = boolean(default=False)
    integer = integer(default=0, min=-10, max=10)
    float = float(default=0.0, min=-10.0, max=100.0)
    string = string(default='string')
[lists]
    booleans = booleans(default=list(False, True))
    integers = integers(default=list(0, 1), min=-10, max=10)
    floats = floats(default=list(0.0), min=-10.0, max=10.0)
    strings = strings(default=list('foo', 'bar'))
""".splitlines()
# This is an example specification file called cfg.ini

# A section with scalar options
[scalars]
    boolean = boolean(default=False) # A boolean value
    integer = integer(default=0, min=-10, max=10) # An integer value between -10 and 10
    float = float(default=0.0, min=-10.0, max=10.0) # A float value between -10.0 and 10.0
    string = string(default='string') # A string value
# A section with list options
[lists]
    booleans = booleans(default=list(False, True)) # A list of booleans
    integers = integers(default=list(0, 1), min=-10, max=10) # A list of integers
    floats = floats(default=list(0.0), min=-10.0, max=10.0) # A list of floats
    strings = strings(default=list('foo', 'bar')) # A list of strings
# This is an example configuration file saved under cfg.cfg

# A section with scalar options
[scalars]
    boolean = True
    integer = 5
    float = 25.0
    string = foo
# A section with list options
[lists]
    floats = 0.0, 1.0, 1.0, 0.0
    booleans = False, True, True, False, True, False
    integers = 0, 1, 1, 0
    strings = foo, bar, bar, foo, foo bar, ' foo bar '
    test = "SaCreBleu!!"   # key which does not exist in cfg.ini
# A section which does not exists in cfg.ini
[new]
    valerie ='aiaiaie'
    bowling = True
# This is an example configuration file saved under cfgplus.cfg

# A section with scalar options
[scalars]
    boolean = True
    integer = 9
    float = 45.0
    string = foo
# A section with list options
[lists]
    floats = 0.0, 1.0, 1.0, 0.0
    booleans = True, False, True, False, True
    integers = 0, 1, 1, 0
    strings = foo, bar, bar, foo, foo bar, ' foo bar ', 'olllle'
    test = "from cfgplus.cfg"
[new]
    valerie ='aiaiaie cfgplus.cfg'
    boul = True
[more]
    youpy = cfgplus
    [[moremore]]
        fin = donecfgplus
[1]:
# Get name of .ini
# __file__ = 'tutovac_optparse.ipynb'
# specfile = '%s.ini'%(P.splitext(__file__)[0])
# cfgfile = '%s.cfg'%(P.splitext(__file__)[0])
[2]:
from osdyn.config import data_sample
specfile = data_sample("cfg.ini")
cfgfile = data_sample("cfg.cfg")

Different types of readings

[3]:
import os, os.path as P, pprint, sys
import osdyn.utils.misc.config as C

read .ini from utils ConfigManager class

[4]:
cfgm = C.ConfigManager(specfile)
[5]:
cfgm
[5]:
<osdyn.utils.misc.config.ConfigManager at 0x2aaad288fd30>

methods for ConfigManager class

[6]:
[v for v in dir(cfgm) if not v.startswith('_')]
[6]:
['arg_long_help',
 'arg_parse',
 'arg_patch',
 'cfgspecs',
 'configspecs',
 'defaults',
 'get_spec',
 'load',
 'patch',
 'reset',
 'specs',
 'validator']

ask for the defaults method to get the content of the .ini cfg file

[7]:
print('\n Using the config manager\n')
cfgini = cfgm.defaults()
print('\n Default config from specfile %r:\n\n%s\n'%(specfile, pprint.pformat(cfgini.dict())))

 Using the config manager


 Default config from specfile '/home7/scratch/oasisdev/OSDYN/osdyn/data/cfg.ini':

{'lists': {'booleans': [False, True],
           'floats': [0.0],
           'integers': [0, 1],
           'strings': ['foo', 'bar']},
 'scalars': {'boolean': False, 'float': 0.0, 'integer': 0, 'string': 'string'}}

read .cfg from with class ConfigObj class

[8]:
# The ConfigManager currently set defaults from its specifications when using its load method.
# So in this example we'll directly use configobj to show you only the content of the configuration file
import configobj
cfgobj = configobj.ConfigObj(cfgfile)
print('\n Content of the config file %r:\n\n%s\n'%(cfgfile, pprint.pformat(cfgobj.dict())))

 Content of the config file '/home7/scratch/oasisdev/OSDYN/osdyn/data/cfg.cfg':

{'lists': {'booleans': ['False', 'True', 'True', 'False', 'True', 'True'],
           'floats': ['0.0', '1.0', '1.0', '0.0'],
           'integers': ['0', '1', '1', '0'],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': 'True',
             'float': '25.0',
             'integer': '5',
             'string': 'foo'}}

Note

all the values are strings

read the same cfg file from ConfigManager

and see what happens

[9]:
cfgc = C.ConfigManager(cfgfile)
[10]:
cfgc.defaults()
[10]:
ConfigObj({'scalars': {}, 'lists': {}, 'new': {}})

Warning

None specification ! You cannot validate your inputs

but you can get the content of the cfg file from the load method

[11]:
# way to get the content of the configuration file (.cfg)
cfgm = C.ConfigManager(specfile)
cfgref = cfgm.load(cfgfile)
print('\n Content of the config file read from utils %r:\n\n%s\n'%(cfgfile, pprint.pformat(cfgref.dict())))

 Content of the config file read from utils '/home7/scratch/oasisdev/OSDYN/osdyn/data/cfg.cfg':

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': [0.0, 1.0, 1.0, 0.0],
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': True, 'float': 25.0, 'integer': 5, 'string': 'foo'}}

Note

Differs from cfgobj as all the value are correctly typed

But let’s see what happens if the cfg file is not correct,
because the type of value is uncorrect
[12]:
cfgt = cfgm.load(data_sample('cfgbad.cfg'))
Config value error: [scalars] integer: the value "9." is of the wrong type.
Setting it to default value: 0
[13]:
print('\n Content of the config file read from utils %r:\n\n%s\n'%(cfgfile, pprint.pformat(cfgt.dict())))

 Content of the config file read from utils '/home7/scratch/oasisdev/OSDYN/osdyn/data/cfg.cfg':

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': [0.0, 1.0, 1.0, 0.0],
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': True, 'float': 45.0, 'integer': 0, 'string': 'foo'}}

Warning

The integer value is returned as 0 because float is not of the correct type defined in .ini

Important

Thanks to ConfigManager and a cfg.ini file, one validates the values passed through cfg file

None validation, the values are all strings consequently

[14]:
cfgnovalid = cfgm.load(cfgfile=data_sample('cfg.cfg'), validate=False)
print('\n Content of the config file, reading without validation %r:\n\n%s\n'%(cfgfile, pprint.pformat(cfgnovalid.dict())))

 Content of the config file, reading without validation '/home7/scratch/oasisdev/OSDYN/osdyn/data/cfg.cfg':

{'lists': {'booleans': ['False', 'True', 'True', 'False', 'True', 'True'],
           'floats': ['0.0', '1.0', '1.0', '0.0'],
           'integers': ['0', '1', '1', '0'],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': 'True',
             'float': '25.0',
             'integer': '5',
             'string': 'foo'}}

[15]:
assert cfgnovalid == cfgobj

patch method

Patch .ini with .cfg

[16]:
cfgm = C.ConfigManager(specfile)
#cfgm.patch(specfile,cfgfile) # same
[17]:
cfgt = cfgm.patch(data_sample('cfg.ini'),data_sample('cfg.cfg'))
print('\n Patch .ini and .cfg :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Patch .ini and .cfg :

{'lists': {'booleans': ['False', 'True', 'True', 'False', 'True', 'True'],
           'floats': ['0.0', '1.0', '1.0', '0.0'],
           'integers': ['0', '1', '1', '0'],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': 'True',
             'float': ['2', '5', '.', '0'],
             'integer': ['5'],
             'string': 'foo'}}

Error

Mais c’est quoi cette décomposition de 25.0 ????

Warning

Patch doit combiner 2 .cfg et non pas un .ini. Donc ici, les types ne sont pas pris en compte. Pas sûre que cela explique le 2 5 . 0 mais en tout cas c’est une erreur d’utiliser pach avec un .ini

The following way to “patch” a .cfg file with a .ini is correct

[18]:
cfgt = cfgm.load(cfgfile=data_sample('cfg.cfg'))
print('\n .cfg only :\n\n%s\n'%(pprint.pformat(cfgm.load(cfgfile=data_sample('cfg.cfg')).dict())))

 .cfg only :

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': [0.0, 1.0, 1.0, 0.0],
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': True, 'float': 25.0, 'integer': 5, 'string': 'foo'}}

[19]:
assert cfgt == cfgref
[20]:
cfgm.cfgspecs
[20]:
ConfigObj({'scalars': {'boolean': 'boolean(default=False)', 'integer': 'integer(default=0, min=-10, max=10)', 'float': 'float(default=0.0, min=-10.0, max=100.0)', 'string': "string(default='string')"}, 'lists': {'booleans': 'booleans(default=list(False, True))', 'integers': 'integers(default=list(0, 1), min=-10, max=10)', 'floats': 'floats(default=list(0.0), min=-10.0, max=10.0)', 'strings': 'strings(default=list(foo,bar))'}})
[21]:
#cfgm.load(cfgfile='cfg.cfg',patch=True)  # specify the name of the patch
cfgt = cfgm.load(cfgfile=data_sample('cfg.cfg'),patch=data_sample('cfgplus.cfg'))
print('\n Patch .ini and cfg.cfg + cfgplus.cfg :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Patch .ini and cfg.cfg + cfgplus.cfg :

{'lists': {'booleans': [True, False, True, False, True],
           'floats': [0.0, 1.0, 1.0, 0.0],
           'integers': [0, 1, 1, 0],
           'strings': ['foo',
                       'bar',
                       'bar',
                       'foo',
                       'foo bar',
                       ' foo bar ',
                       'olllle'],
           'test': 'from cfgplus.cfg'},
 'more': {'moremore': {'fin': 'donecfgplus'}, 'youpy': 'cfgplus'},
 'new': {'boul': 'True', 'bowling': 'True', 'valerie': 'aiaiaie cfgplus.cfg'},
 'scalars': {'boolean': True, 'float': 45.0, 'integer': 9, 'string': 'foo'}}

Note

The patch works well, even for new keys and sections. Fine !

Patch 2 .cfg without .ini

[22]:
cfgm = C.ConfigManager(cfgfile)
[23]:
cfgt = cfgm.patch(cfgfile,data_sample('cfgplus.cfg'))
print('\n Patch 2 .cfg without .ini :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Patch 2 .cfg without .ini :

{'lists': {'booleans': ['True', 'False', 'True', 'False', 'True'],
           'floats': ['0.0', '1.0', '1.0', '0.0'],
           'integers': ['0', '1', '1', '0'],
           'strings': ['foo',
                       'bar',
                       'bar',
                       'foo',
                       'foo bar',
                       ' foo bar ',
                       'olllle'],
           'test': 'from cfgplus.cfg'},
 'more': {'moremore': {'fin': 'donecfgplus'}, 'youpy': 'cfgplus'},
 'new': {'boul': 'True', 'bowling': 'True', 'valerie': 'aiaiaie cfgplus.cfg'},
 'scalars': {'boolean': 'True',
             'float': '45.0',
             'integer': '9',
             'string': 'foo'}}

Warning

Works but variables remain as strings

argparse method

Other way to patch .ini with .cfg

[24]:
del cfgm
cfgm = C.ConfigManager(specfile)
Je ne comprends pas : cette partie passe… ou pas !
Si erreur je me retrouve avec les erreurs par défaut.
???
[25]:
cfgt1 = cfgm.arg_parse(cfgfile=data_sample('cfg.cfg'), args=[], extraopts=None) # cfgfilepatch='before by default' = True
print('\n Patch .ini with .cfg file :\n\n%s\n'%(pprint.pformat(cfgt1.dict())))

 Patch .ini with .cfg file :

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': [0.0, 1.0, 1.0, 0.0],
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': False, 'float': 25.0, 'integer': 5, 'string': 'foo'}}

[26]:
def check_dict(dnew, dref):
    for sec in dnew.keys():
        try:
            if dnew[sec] != dref[sec]:
                print(sec, "\n cfgnew: {}, \n cfgref : {}".format(dnew[sec].dict(), dref[sec].dict()))
        except:
            print("Error for section", sec)
[27]:
check_dict(cfgt1, cfgref)
scalars
 cfgnew: {'boolean': False, 'integer': 5, 'float': 25.0, 'string': 'foo'},
 cfgref : {'boolean': True, 'integer': 5, 'float': 25.0, 'string': 'foo'}

Caution

Le booléen passe à False à cause de la ligne 1032 dans utils.misc.config:

! Feed config with command line options:
defaults.walk(_walker_argcfg_setcfg_, raise_errors=True, call_on_sections=False, cfg=cfg, options=options, nested=nested)

???

Update the specfile (.ini) with arguments passed to the script

[28]:
argv = ['--scalars-string=foo,hello world', '--lists-floats=42,3.14', '--scalars-float=155.']
[29]:
cfgm = C.ConfigManager(specfile)
cfi1 = cfgm.arg_parse(args=argv)
print('\n Patch .ini with arguments (already existing in .ini file) :\n\n%s\n'%(pprint.pformat(cfi1.dict())))
print('\n cfgfilepatch=before by default')

 Patch .ini with arguments (already existing in .ini file) :

{'lists': {'booleans': [False, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1],
           'strings': ['foo', 'bar']},
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 0,
             'string': 'foo,hello world'}}


 cfgfilepatch=before by default
[30]:
# Make sure the differences are normal
check_dict(cfi1, cfgini)
scalars
 cfgnew: {'boolean': False, 'integer': 0, 'float': 155.0, 'string': 'foo,hello world'},
 cfgref : {'boolean': False, 'integer': 0, 'float': 0.0, 'string': 'string'}
lists
 cfgnew: {'booleans': [False, True], 'integers': [0, 1], 'floats': [42.0, 3.14], 'strings': ['foo', 'bar']},
 cfgref : {'booleans': [False, True], 'integers': [0, 1], 'floats': [0.0], 'strings': ['foo', 'bar']}

Important

When the arguments do not exist in .ini specfile, you must pass them through extraopts options of arg_parse

[31]:
argv += ['--newarg=tototutu']
[32]:
def split_args(argv):
    # Get the arguments passed to the script
    opts = []
    for v in argv:
        k,kv = v.split("=")
        opts.append( (k,{'default':kv}) )

    # Get the list of keys in cfg file
    keypaths = C.get_key_paths( cfgm.defaults() )

    # Keep only the arguments which are not in ConfigObj file
    extraopts = []
    for arg in opts:
        if arg[0] not in keypaths:
            extraopts.append(arg)

    return opts, extraopts

To save only the argument passed to the script

[33]:
opts, extraopts = split_args(argv)
[34]:
# cfi + args
cfi2 = cfgm.arg_parse(args=argv, extraopts=extraopts)
print('\n Patch .ini with arguments (one does not exist in .ini file) :\n\n%s\n'%(pprint.pformat(cfi2.dict())))
print('\n cfgfilepatch=before by default')

 Patch .ini with arguments (one does not exist in .ini file) :

{'lists': {'booleans': [False, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1],
           'strings': ['foo', 'bar']},
 'newarg': 'tototutu',
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 0,
             'string': 'foo,hello world'}}


 cfgfilepatch=before by default
[35]:
# Make sure the differences are relative to newarg
check_dict(cfi2, cfi1)
Error for section newarg

Tip

Note of the use of cfgfilepatch argument to arg_parse method

    - **cfgfilepatch**: specify if the returned config must be patched if a config file command
            line option is provided and when to patch it. Can take the following values:
            - True or 'before': the config file would be used before command line options
            - 'after': the config file would be used after command line options
            - Any False like value: the config file would not be used
with
    - **cfgfile**: name of the .cfg to be patched (config.cfg by default)

Use **cfgfilepatch** and **cfgfile** together

Important

cfgfilepatch=‘before by default’ = True

[36]:
# arguments only
cfi2b = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfilepatch=False)
print('\n Arguments only (.ini not patched) :\n\n%s\n'%(pprint.pformat(cfi2b.dict())))
print('\n cfgfilepatch=False')

 Arguments only (.ini not patched) :

{'lists': {'floats': [42.0, 3.14]},
 'newarg': 'tototutu',
 'scalars': {'boolean': False, 'float': 155.0, 'string': 'foo,hello world'}}


 cfgfilepatch=False

Only the arguments are introduced into the cfg object

[37]:
# cfi + args
cfi2c = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfilepatch='before')
# Make sure there is no difference
check_dict(cfi2, cfi2c)

Note

42 is out of range !

- cfgfilepatch = False . No use of .ini to valid .cfg (because no .cfg file) and arguments
- cfgfilepatch = True or 'before', .cdf patched with .ini before using values passed through arguments
- cfgfilepatch = 'after', patch with .ini after using values passed through arguments
[38]:
# args + .ini (because .cfg does not exists) patched (overwriten) after.
cfi2d = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfilepatch='after')
print('\n .ini (because .cfg does not exists) overwrites args :\n\n%s\n'%(pprint.pformat(cfi2d.dict())))
print('\n cfgfilepatch=after')

 .ini (because .cfg does not exists) overwrites args :

{'lists': {'booleans': [False, True],
           'floats': [0.0],
           'integers': [0, 1],
           'strings': ['foo', 'bar']},
 'newarg': 'tototutu',
 'scalars': {'boolean': False, 'float': 0.0, 'integer': 0, 'string': 'string'}}


 cfgfilepatch=after
[39]:
# .ini except for additionnal key,value
check_dict(cfi2d, cfgini)
Error for section newarg

Important

Back to default values even for scalars-string. I mean : back to all the default values (except the unkown ones) because the cfgfile is not specified !!!(‘config.cfg’ by default)

also update with .cfg file

[40]:
# args + .cfg, in this order
cfi2e = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfilepatch='after', cfgfile=data_sample('cfg.cfg'))
print('\n args + .cfg, in this order :\n\n%s\n'%(pprint.pformat(cfi2e.dict())))
print('\n cfgfilepatch=after + correct cfgfile')

 args + .cfg, in this order :

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': [0.0, 1.0, 1.0, 0.0],
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'newarg': 'tototutu',
 'scalars': {'boolean': True, 'float': 25.0, 'integer': 5, 'string': 'foo'}}


 cfgfilepatch=after + correct cfgfile
[41]:
# Make sure the difference concerns the new key,value
check_dict(cfi2e, cfgref)
Error for section newarg

Important

As cfg.cfg is patched at the end of the process, only the new key,value (undefined in .cfg) is taken into account

[42]:
cfi2f = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfile=data_sample('cfg.cfg'))
print('\n Patch .cfg with arguments (one does not exist in .ini file) :\n\n%s\n'%(pprint.pformat(cfi2f.dict())))
print('\n cfgfilepatch=before + correct cfgfile')

 Patch .cfg with arguments (one does not exist in .ini file) :

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'newarg': 'tototutu',
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 5,
             'string': 'foo,hello world'}}


 cfgfilepatch=before + correct cfgfile
[43]:
# Make sure the difference concerns the arguments only
check_dict(cfi2f, cfgref)
Error for section newarg
scalars
 cfgnew: {'boolean': False, 'integer': 5, 'float': 155.0, 'string': 'foo,hello world'},
 cfgref : {'boolean': True, 'integer': 5, 'float': 25.0, 'string': 'foo'}
lists
 cfgnew: {'booleans': [False, True, True, False, True, True], 'integers': [0, 1, 1, 0], 'floats': [42.0, 3.14], 'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '], 'test': 'SaCreBleu!!'},
 cfgref : {'floats': [0.0, 1.0, 1.0, 0.0], 'booleans': [False, True, True, False, True, True], 'integers': [0, 1, 1, 0], 'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '], 'test': 'SaCreBleu!!'}

Again ‘boolean’: False !!!

Check arg_parse arguments

Tip

Argument to arg_parse method : cfgfilepatch

- **cfgfilepatch**: specify if the returned config must be patched if a config file command
        line option is provided and when to patch it. Can take the following values:
        - True or 'before': the config file would be used before command line options
        - 'after': the config file would be used after command line options
        - Any False like value: the config file would not be used

Important

cfgfilepatch=‘before by default’ = True

[44]:
cfgt = cfgm.arg_parse(args=argv, extraopts=extraopts, getargs=True) # cfgfilepatch='before by default' = True
namespace = cfgt[1]  # get the passed arguments
cfgt = cfgt[0]
print('\n Patch .ini with arguments (one does not exist in .ini file) :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Patch .ini with arguments (one does not exist in .ini file) :

{'lists': {'booleans': [False, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1],
           'strings': ['foo', 'bar']},
 'newarg': 'tototutu',
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 0,
             'string': 'foo,hello world'}}

Warning

Ici je n’ai pas entré le .cfg car le nom du fichier par défaut config.cfg n’existe pas

[45]:
print('\n arguments passed to the script :\n\n{}\n'.format(namespace))

 arguments passed to the script :

Namespace(newarg='tototutu', cfgfile='config.cfg', scalars_boolean=False, scalars_integer=None, scalars_float=155.0, scalars_string='foo,hello world', lists_booleans=None, lists_integers=None, lists_floats=[42.0, 3.14], lists_strings=None, vacumm_cfg=ConfigObj({'newarg': 'tototutu', 'scalars': {'boolean': False, 'integer': 0, 'float': 155.0, 'string': 'foo,hello world'}, 'lists': {'booleans': [False, True], 'integers': [0, 1], 'floats': [42.0, 3.14], 'strings': ['foo', 'bar']}}))

Tip

Argument to arg_parse method: getargs

- **getargs**: allow getting the parsed arguments in addition to the config if parse=True

Note

You get a tuple (cfg,namespace) of the patched cfg file and all the keys of the .cfg file under a Namespace format (so you get more than the arguments of the script).

difference arg_parse and patch

Tip

Argument to arg_parse method: patch

    - **cfgfilepatch**: specify if the returned config must be patched if a config file command
            line option is provided and when to patch it. Can take the following values:
            - True or 'before': the config file would be used before command line options
            - 'after': the config file would be used after command line options
            - Any False like value: the config file would not be used

with
    - **cfgfile**: name of the .cfg to be patched
- **patch**, optional: used if parse is True.
        Can take the following values:
        - a :class:`bool` value indicating wheter to apply defaults on the returned config
          before applying the command line config
        - a :class:`ConfigObj` instance to apply on the returned config
          before applying the command line config
[46]:
# patch .ini cfg.cfg + arguments
cfgarg = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfile=data_sample('cfg.cfg'))
[47]:
# no difference
check_dict(cfgarg, cfi2f)
[48]:
# patch .ini cfg.cfg + arguments
cfgargb = cfgm.arg_parse(args=argv, extraopts=extraopts, cfgfile=cfgref)  # cfjobj
[49]:
# no difference
check_dict(cfgarg, cfgargb)

Important

At this point, one patches .ini with .cfg and then with arguments passed to the script. The new arguments are taken into account (correction config.py in utils)

[50]:
# patch .ini + arguments but not cfg.cfg because one must fill with a configobj
cfgt = cfgm.arg_parse(args=argv, extraopts=extraopts, patch=data_sample('cfg.cfg'))
[51]:
# No difference (.ini + args)
check_dict(cfgt, cfi2c)
[52]:
# patch .ini cfg.cfg (not present) + arguments
cfgt = cfgm.arg_parse(args=argv, extraopts=extraopts, patch=cfgref)
print('\n ERROR in patch .cfg with arguments (one does not exist in .ini file) :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 ERROR in patch .cfg with arguments (one does not exist in .ini file) :

{'lists': {'booleans': [False, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1],
           'strings': ['foo', 'bar'],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'newarg': 'tototutu',
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 0,
             'string': 'foo,hello world'}}

Warning

args + .cfg (only for key,value unknown in .ini) + .ini due to missing cfg.cfg because cfgfile not present (config.cfg by default so the default (.ini) is taken into account

[53]:
# patch .ini cfg.cfg + arguments
cfgt2 = cfgm.arg_parse(args=argv, extraopts=extraopts, patch=cfgref, cfgfilepath=False, cfgfile=cfgref)
print('\n Patch .cfg with arguments (one does not exist in .ini file) :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Patch .cfg with arguments (one does not exist in .ini file) :

{'lists': {'booleans': [False, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1],
           'strings': ['foo', 'bar'],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'newarg': 'tototutu',
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 0,
             'string': 'foo,hello world'}}

Note

patch+cfgfilepatch=False+cfgfile and cfgfilepatch='before'[+cfgfile] have the same behabior

Update the cfg file (.cfg) with arguments passed to the script

[54]:
cfgm = C.ConfigManager(cfgfile)

add arguments

[55]:
argv = ['--scalars-string=foo,hello world', '--lists-floats=42,3.14', '--scalars-float=155.']

Warning

When no .ini, all the arguments are extra options. Even the ones in the .cfg file. It is the way utils.misc.config.CongifManager works, due to as call to parser.parse_args(lists(args)). You must have added extra arguments to the parser at first.

[56]:
# Get the arguments passed to the script
opts = []
for v in argv:
    k,kv = v.split("=")
    opts.append( (k,{'default':kv}) )

# Keep only the arguments which are not in ConfigObj file
extraopts = []
for arg in opts:
    #if arg[0] not in keypaths:
    extraopts.append(arg)
[57]:
extraopts
[57]:
[('--scalars-string', {'default': 'foo,hello world'}),
 ('--lists-floats', {'default': '42,3.14'}),
 ('--scalars-float', {'default': '155.'})]
[58]:
argv
[58]:
['--scalars-string=foo,hello world',
 '--lists-floats=42,3.14',
 '--scalars-float=155.']
[59]:
cfgref
[59]:
ConfigObj({'scalars': {'boolean': True, 'integer': 5, 'float': 25.0, 'string': 'foo'}, 'lists': {'floats': [0.0, 1.0, 1.0, 0.0], 'booleans': [False, True, True, False, True, True], 'integers': [0, 1, 1, 0], 'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '], 'test': 'SaCreBleu!!'}, 'new': {'valerie': 'aiaiaie', 'bowling': 'True'}})
[60]:
kwargs = {'validate':False}
cfgarg = cfgm.arg_parse(cfgfile=cfgref, args=[], extraopts=extraopts, **kwargs)
print('\n patch .cfg with arguments (no .ini) :\n\n%s\n'%(pprint.pformat(cfgarg.dict())))

 patch .cfg with arguments (no .ini) :

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': '42,3.14',
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': True,
             'float': '155.',
             'integer': 5,
             'string': 'foo,hello world'}}

[61]:
# Difference in types
check_dict(cfgarg, cfi2f)
scalars
 cfgnew: {'boolean': True, 'integer': 5, 'float': '155.', 'string': 'foo,hello world'},
 cfgref : {'boolean': False, 'integer': 5, 'float': 155.0, 'string': 'foo,hello world'}
lists
 cfgnew: {'floats': '42,3.14', 'booleans': [False, True, True, False, True, True], 'integers': [0, 1, 1, 0], 'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '], 'test': 'SaCreBleu!!'},
 cfgref : {'booleans': [False, True, True, False, True, True], 'integers': [0, 1, 1, 0], 'floats': [42.0, 3.14], 'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '], 'test': 'SaCreBleu!!'}

Important

keep .ini because the arguments are not set to the correct type otherwise !

[62]:
# note required kwargs = {'validate':False}
cfgarg2 = cfgm.arg_parse(cfgfile=cfgref, args=[], extraopts=extraopts)
print('\n patch .cfg with arguments (no .ini) :\n\n%s\n'%(pprint.pformat(cfgarg.dict())))

 patch .cfg with arguments (no .ini) :

{'lists': {'booleans': [False, True, True, False, True, True],
           'floats': '42,3.14',
           'integers': [0, 1, 1, 0],
           'strings': ['foo', 'bar', 'bar', 'foo', 'foo bar', ' foo bar '],
           'test': 'SaCreBleu!!'},
 'new': {'bowling': 'True', 'valerie': 'aiaiaie'},
 'scalars': {'boolean': True,
             'float': '155.',
             'integer': 5,
             'string': 'foo,hello world'}}

Config value error: [lists] booleans: the check "False, True, True, False, True, True" is unknown.
Setting it to default value: None
Config value error: [lists] floats: the check "0.0, 1.0, 1.0, 0.0" is unknown.
Setting it to default value: None
Config value error: [lists] integers: the check "0, 1, 1, 0" is unknown.
Setting it to default value: None
Config value error: [lists] strings: the check "foo, bar, bar, foo, foo bar, ' foo bar '" is unknown.
Setting it to default value: None
Config value error: [lists] test: the check "SaCreBleu!!" is unknown.
Setting it to default value: None
Config value error: [new] bowling: the check "True" is unknown.
Setting it to default value: None
Config value error: [new] valerie: the check "aiaiaie" is unknown.
Setting it to default value: None
Config value error: [scalars] boolean: the check "True" is unknown.
Setting it to default value: None
Config value error: [scalars] float: the check "25.0" is unknown.
Setting it to default value: None
Config value error: [scalars] integer: the check "5" is unknown.
Setting it to default value: None
Config value error: [scalars] string: the check "foo" is unknown.
Setting it to default value: None

Use only arguments passed to the script (nor .ini neither .cfg file)

[63]:
import os, os.path as P, pprint, sys
import osdyn.utils.misc.config as C
[64]:
cfgm = C.ConfigManager()
[65]:
cfgm.defaults()
[65]:
ConfigObj({})
[66]:
argv = ['--scalars-string=foo,hello world', '--lists-floats=42,3.14', '--scalars-float=155.']
[67]:
# Nor .ini neither .cfg, so all the arguments are extra options
extraopts = []
for v in argv:
    k,kv = v.split("=")
    extraopts.append( (k,{'default':kv}) )
[68]:
extraopts
[68]:
[('--scalars-string', {'default': 'foo,hello world'}),
 ('--lists-floats', {'default': '42,3.14'}),
 ('--scalars-float', {'default': '155.'})]
[69]:
cfgt = cfgm.arg_parse(cfgfile=cfgm.defaults(), args=argv, extraopts=extraopts, cfgfilepatch=True)
print('\n Save arguments in a new cfg :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Save arguments in a new cfg :

{'lists': {'floats': '42,3.14'},
 'scalars': {'float': '155.', 'string': 'foo,hello world'}}

osdyn.utils.misc.config list_options

[70]:
print('\n :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 :

{'lists': {'floats': '42,3.14'},
 'scalars': {'float': '155.', 'string': 'foo,hello world'}}

[71]:
from osdyn.utils.misc.config import list_options
[72]:
list_options(cfgt)
[72]:
[(['scalars'], 'string'), (['scalars'], 'float'), (['lists'], 'floats')]
[73]:
list_options(cfgt, sections=True) #'new')
[73]:
[(['scalars'], None),
 (['scalars'], 'string'),
 (['scalars'], 'float'),
 (['lists'], None),
 (['lists'], 'floats')]
[74]:
list_options(cfgt, parents=['moremore'])
[74]:
[(['moremore', 'scalars'], 'string'),
 (['moremore', 'scalars'], 'float'),
 (['moremore', 'lists'], 'floats')]
[75]:
list_options(cfgt, values=True)
[75]:
[(['scalars'], 'string', 'foo,hello world'),
 (['scalars'], 'float', '155.'),
 (['lists'], 'floats', '42,3.14')]
[76]:
list_options(cfgt, values=True, parents=['moremore'])
[76]:
[(['moremore', 'scalars'], 'string', 'foo,hello world'),
 (['moremore', 'scalars'], 'float', '155.'),
 (['moremore', 'lists'], 'floats', '42,3.14')]

Create documentation from .cfg .ini file plus arguments

[77]:
argv = ['--scalars-string=foo,hello world', '--lists-floats=42,3.14', '--scalars-float=155.']
[78]:
cfgm = C.ConfigManager(specfile)
cfgt = cfgm.arg_parse(args=argv)
print('\n Patch .ini with arguments (already existing in .ini file) :\n\n%s\n'%(pprint.pformat(cfgt.dict())))

 Patch .ini with arguments (already existing in .ini file) :

{'lists': {'booleans': [False, True],
           'floats': [42.0, 3.14],
           'integers': [0, 1],
           'strings': ['foo', 'bar']},
 'scalars': {'boolean': False,
             'float': 155.0,
             'integer': 0,
             'string': 'foo,hello world'}}

Error

A partir de là cela foire. Il faudrait que je comprenne comment cela marche pour faire la doc mais ras le bol.

cfg = argcfg = cfgm.arg_parse(args=[‘–help’])

print(‘\n*** Command line config only:\n\n`%s:nbsphinx-math:n`’%(pprint.pformat(cfg.dict())))

cfg = argcfg = cfgm.arg_parse(args=[‘–help’])

print(‘\n*** Command line config only:\n\n`%s:nbsphinx-math:n`’%(pprint.pformat(cfg.dict())))