Using LayeredConfig with argparse
¶
The standard module for handling command line arguments in python is
argparse
. This module handles much of the same things as
LayeredConfig does (eg. defining the default values and types of
arguments and making them easily accessed), but it isn’t able to read
parameter values from other sources such as INI files or environment
variables.
LayeredConfig integrates with argparse through the
Commandline
config source. If you have
existing code to set up an argparse.ArgumentParser
object,
you can re-use that with LayeredConfig.
import sys
import argparse
from datetime import date, datetime
from layeredconfig import LayeredConfig, Defaults, INIFile, Commandline, UNIT_SEP
After this setup, you might want to create any number of config
sources. In this example we use a Defaults
object, mostly used for specifying the type of different arguments.
defaults = Defaults({'home': str,
'name': 'MyApp',
'dostuff': bool,
'times': int,
'duedate': date,
'things': list,
'submodule': {'retry': bool,
'lastrun': datetime
}
})
And also an INIFile
that is used to store
actual values for most parameters.
with open("myapp.ini", "w") as fp:
fp.write("""[__root__]
home = /tmp/myapp
dostuff = False
times = 4
duedate = 2014-10-30
things = Huey, Dewey, Louie
[submodule]
retry = False
lastrun = 2014-10-30 16:40:22
""")
inifile = INIFile("myapp.ini")
Next up, we create an instance of argparse.ArgumentParser
in the normal way. Note that in this example, we specify the types of
some of the parameters, since this is representative of how
ArgumentParser normally is used. But you can also omit this
information (the action
and type
parameters to
add_argument()
) as long as you
provide information through a Defaults config source object.
Note: we don’t add arguments for --duedate
or --submodule-lastrun
to
show that LayeredConfig can define these arguments based on other
sources. Also note that defaults values are automatically fetched from
either defaults or inifile.
parser = argparse.ArgumentParser("This is a simple program")
parser.add_argument("--home", help="The home directory of the app")
parser.add_argument('--dostuff', action="store_true", help="Do some work")
parser.add_argument("-t", "--times", type=int, help="Number of times to do it")
parser.add_argument('--things', action="append", help="Extra things to crunch")
parser.add_argument('--retry', action="store_true", help="Try again")
parser.add_argument("file", metavar="FILE", help="The filename to process")
Now, instead of calling
parse_args()
, you can pass this
initialized parser object as a named parameter when creating a
Commandline
source, and use this to create
a LayeredConfig
object.
Note that you can use short parameters if you want, as long as you define long parameters (that map to your other parameter names) as well
sys.argv = ['./myapp.py', '--home=/opt/myapp', '-t=2', '--dostuff', 'file.txt']
cfg = LayeredConfig(defaults,
inifile,
Commandline(parser=parser))
print("Starting %s in %s for %r times (doing work: %s)" % (cfg.name,
cfg.home,
cfg.times,
cfg.dostuff))
# should print "Starting MyApp in /opt/myapp for 2 times (doing work: True)"
The standard feature of argparse to create a help text if the -h
parameter is given still exists. Note that it will also display
parameters such as –name`, which was defined in the
Defaults
object, not in the parser object.
sys.argv = ['./myapp.py', '-h']
cfg = LayeredConfig(defaults,
inifile,
Commandline(parser=parser))
Warning
Using a bespoke argparse.ArgumentParser
together with
subsections is a bit more complicated. If you want to do that, you
will need to setup each argument to the ArgumenParser object by
explicitly naming the internal name for the attribute as specifid
by the dest parameter, and separating the subsections with the
special layeredconfig.UNIT_SEP
delimiter, eg:
parser.add_argument("--submodule-retry", help="Whether to retry the submodule",
dest="submodule"+UNIT_SEP+"retry")