Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which is the best way to allow configuration options be overridden at the command line in Python?

I have a Python application which needs quite a few (~30) configuration parameters. Up to now, I used the OptionParser class to define default values in the app itself, with the possibility to change individual parameters at the command line when invoking the application.

Now I would like to use 'proper' configuration files, for example from the ConfigParser class. At the same time, users should still be able to change individual parameters at the command line.

I was wondering if there is any way to combine the two steps, e.g. use optparse (or the newer argparse) to handle command line options, but reading the default values from a config file in ConfigParse syntax.

Any ideas how to do this in an easy way? I don't really fancy manually invoking ConfigParse, and then manually setting all defaults of all the options to the appropriate values...

like image 370
andreas-h Avatar asked Aug 31 '10 14:08

andreas-h


People also ask

How do I create a config file in Python?

Python Configuration File The simplest way to write configuration files is to simply write a separate file that contains Python code. You might want to call it something like databaseconfig.py . Then you could add the line *config.py to your . gitignore file to avoid uploading it accidentally.

What is CFG in Python?

Python ConfigParser ConfigParser is a Python class which implements a basic configuration language for Python programs. It provides a structure similar to Microsoft Windows INI files. ConfigParser allows to write Python programs which can be customized by end users easily.


1 Answers

I just discovered you can do this with argparse.ArgumentParser.parse_known_args(). Start by using parse_known_args() to parse a configuration file from the commandline, then read it with ConfigParser and set the defaults, and then parse the rest of the options with parse_args(). This will allow you to have a default value, override that with a configuration file and then override that with a commandline option. E.g.:

Default with no user input:

$ ./argparse-partial.py Option is "default" 

Default from configuration file:

$ cat argparse-partial.config  [Defaults] option=Hello world! $ ./argparse-partial.py -c argparse-partial.config  Option is "Hello world!" 

Default from configuration file, overridden by commandline:

$ ./argparse-partial.py -c argparse-partial.config --option override Option is "override" 

argprase-partial.py follows. It is slightly complicated to handle -h for help properly.

import argparse import ConfigParser import sys  def main(argv=None):     # Do argv default this way, as doing it in the functional     # declaration sets it at compile time.     if argv is None:         argv = sys.argv      # Parse any conf_file specification     # We make this parser with add_help=False so that     # it doesn't parse -h and print help.     conf_parser = argparse.ArgumentParser(         description=__doc__, # printed with -h/--help         # Don't mess with format of description         formatter_class=argparse.RawDescriptionHelpFormatter,         # Turn off help, so we print all options in response to -h         add_help=False         )     conf_parser.add_argument("-c", "--conf_file",                         help="Specify config file", metavar="FILE")     args, remaining_argv = conf_parser.parse_known_args()      defaults = { "option":"default" }      if args.conf_file:         config = ConfigParser.SafeConfigParser()         config.read([args.conf_file])         defaults.update(dict(config.items("Defaults")))      # Parse rest of arguments     # Don't suppress add_help here so it will handle -h     parser = argparse.ArgumentParser(         # Inherit options from config_parser         parents=[conf_parser]         )     parser.set_defaults(**defaults)     parser.add_argument("--option")     args = parser.parse_args(remaining_argv)     print "Option is \"{}\"".format(args.option)     return(0)  if __name__ == "__main__":     sys.exit(main()) 
like image 149
Von Avatar answered Sep 17 '22 08:09

Von