README.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. Overview
  2. ~~~~~~~~
  3. Applications with more than a handful of user-settable options are best
  4. configured through a combination of command line args, config files,
  5. hard-coded defaults, and in some cases, environment variables.
  6. Python's command line parsing modules such as argparse have very limited
  7. support for config files and environment variables, so this module
  8. extends argparse to add these features.
  9. |Travis CI Status for bw2/ConfigArgParse| -- from `Travis CI <https://travis-ci.org/bw2/ConfigArgParse/>`_
  10. Features
  11. ~~~~~~~~
  12. - command-line, config file, env var, and default settings can now be
  13. defined, documented, and parsed in one go using a single API (if a
  14. value is specified in more than one way then: command line >
  15. environment variables > config file values > defaults)
  16. - config files can have .ini or .yaml style syntax (eg. key=value or
  17. key: value)
  18. - user can provide a config file via a normal-looking command line arg
  19. (eg. -c path/to/config.txt) rather than the argparse-style @config.txt
  20. - one or more default config file paths can be specified
  21. (eg. ['/etc/bla.conf', '~/.my_config'] )
  22. - all argparse functionality is fully supported, so this module can
  23. serve as a drop-in replacement (verified by argparse unittests).
  24. - env vars and config file keys & syntax are automatically documented
  25. in the -h help message
  26. - new method :code:`print_values()` can report keys & values and where
  27. they were set (eg. command line, env var, config file, or default).
  28. - lite-weight (no 3rd-party library dependencies except (optionally) PyYAML)
  29. - extensible (:code:`ConfigFileParser` can be subclassed to define a new
  30. config file format)
  31. - unittested by running the unittests that came with argparse but on
  32. configargparse, and using tox to test with python2.7+ and python3+
  33. Example
  34. ~~~~~~~
  35. *my_script.py*:
  36. Script that defines 4 options and a positional arg and then parses and prints the values. Also,
  37. it prints out the help message as well as the string produced by :code:`format_values()` to show
  38. what they look like.
  39. .. code:: py
  40. import configargparse
  41. p = configargparse.ArgParser(default_config_files=['/etc/settings.ini', '~/.my_settings'])
  42. p.add('-c', '--my-config', required=True, is_config_file=True, help='config file path')
  43. p.add('--genome', required=True, help='path to genome file') # this option can be set in a config file because it starts with '--'
  44. p.add('-v', help='verbose', action='store_true')
  45. p.add('-d', '--dbsnp', help='known variants .vcf', env_var='DBSNP_PATH') # this option can be set in a config file because it starts with '--'
  46. p.add('vcf', nargs='+', help='variant file(s)')
  47. options = p.parse_args()
  48. print(options)
  49. print("----------")
  50. print(p.format_help())
  51. print("----------")
  52. print(p.format_values()) # useful for logging where different settings came from
  53. *config.txt:*
  54. Since the script above set the config file as required=True, lets create a config file to give it:
  55. .. code:: py
  56. # settings for my_script.py
  57. genome = HCMV # cytomegalovirus genome
  58. dbsnp = /data/dbsnp/variants.vcf
  59. *command line:*
  60. Now run the script and pass it the config file:
  61. .. code:: bash
  62. python my_script.py --genome hg19 --my-config config.txt f1.vcf f2.vcf
  63. *output:*
  64. Here is the result:
  65. .. code:: bash
  66. Namespace(dbsnp='/data/dbsnp/variants.vcf', genome='hg19', my_config='config.txt', vcf=['f1.vcf', 'f2.vcf'], verbose=False)
  67. ----------
  68. usage: my_script.py [-h] --genome GENOME [-v] -c MY_CONFIG [-d DBSNP]
  69. vcf [vcf ...]
  70. Args that start with '--' (eg. --genome) can also be set in a config file
  71. (/etc/settings.ini or /home/jeff/.my_settings or provided via -c) by using
  72. .ini or .yaml-style syntax (eg. genome=value). Command-line values override
  73. environment variables which override config file values which override
  74. defaults.
  75. positional arguments:
  76. vcf variant file
  77. optional arguments:
  78. -h, --help show this help message and exit
  79. --genome GENOME path to genome file
  80. -v verbose
  81. -c MY_CONFIG, --my-config MY_CONFIG
  82. config file path
  83. -d DBSNP, --dbsnp DBSNP
  84. known variants .vcf [env var: DBSNP_PATH]
  85. ----------
  86. Command Line Args: --genome hg19 --my-config config.txt f1.vcf f2.vcf
  87. Config File (config.txt):
  88. dbsnp: /data/dbsnp/variants.vcf
  89. Special Values
  90. ~~~~~~~~~~~~~~
  91. Under the hood, configargparse handles environment variables and config file
  92. values by converting them to their corresponding command line arg. For
  93. example, "key = value" will be processed as if "--key value" was specified
  94. on the command line.
  95. Also, the following special values (whether in a config file or an environment
  96. variable) are handled in a special way to support booleans and lists:
  97. - :code:`key = true` is handled as if "--key" was specified on the command line.
  98. In your python code this key must be defined as a boolean flag
  99. (eg. action="store_true" or similar).
  100. - :code:`key = [value1, value2, ...]` is handled as if "--key value1 --key value2"
  101. etc. was specified on the command line. In your python code this key must
  102. be defined as a list (eg. action="append").
  103. Config File Syntax
  104. ~~~~~~~~~~~~~~~~~~
  105. Only command line args that have a long version (eg. one that starts with '--')
  106. can be set in a config file. For example, "--color" can be set by
  107. putting "color=green" in a config file. The config file syntax depends on the
  108. constuctor arg: :code:`config_file_parser_class` which can be set to one of the
  109. provided classes: :code:`DefaultConfigFileParser` or :code:`YAMLConfigFileParser`,
  110. or to your own subclass of the :code:`ConfigFileParser` abstract class.
  111. *DefaultConfigFileParser* - the full range of valid syntax is:
  112. .. code:: yaml
  113. # this is a comment
  114. ; this is also a comment (.ini style)
  115. --- # lines that start with --- are ignored (yaml style)
  116. -------------------
  117. [section] # .ini-style section names are treated as comments
  118. # how to specify a key-value pair (all of these are equivalent):
  119. name value # key is case sensitive: "Name" isn't "name"
  120. name = value # (.ini style) (white space is ignored, so name = value same as name=value)
  121. name: value # (yaml style)
  122. --name value # (argparse style)
  123. # how to set a flag arg (eg. arg which has action="store_true")
  124. --name
  125. name
  126. name = True # "True" and "true" are the same
  127. # how to specify a list arg (eg. arg which has action="append")
  128. fruit = [apple, orange, lemon]
  129. indexes = [1, 12, 35 , 40]
  130. *YAMLConfigFileParser* - allows a subset of YAML syntax (http://goo.gl/VgT2DU)
  131. .. code:: yaml
  132. # a comment
  133. name1: value
  134. name2: true # "True" and "true" are the same
  135. fruit: [apple, orange, lemon]
  136. indexes: [1, 12, 35, 40]
  137. ArgParser Singletons
  138. ~~~~~~~~~~~~~~~~~~~~~~~~~
  139. To make it easier to configure different modules in an application,
  140. configargparse provides globally-available ArgumentParser instances
  141. via configargparse.getArgumentParser('name') (similar to
  142. logging.getLogger('name')).
  143. Here is an example of an application with a utils module that also
  144. defines and retrieves its own command-line args.
  145. *main.py*
  146. .. code:: py
  147. import configargparse
  148. import utils
  149. p = configargparse.getArgumentParser()
  150. p.add_argument("-x", help="Main module setting")
  151. p.add_argument("--m-setting", help="Main module setting")
  152. options = p.parse_known_args() # using p.parse_args() here may raise errors.
  153. *utils.py*
  154. .. code:: py
  155. import configargparse
  156. p = configargparse.getArgumentParser()
  157. p.add_argument("--utils-setting", help="Config-file-settable option for utils")
  158. if __name__ == "__main__":
  159. options = p.parse_known_args()
  160. Help Formatters
  161. ~~~~~~~~~~~~~~~
  162. :code:`ArgumentDefaultsRawHelpFormatter` is a new HelpFormatter that both adds
  163. default values AND disables line-wrapping. It can be passed to the constructor:
  164. :code:`ArgParser(.., formatter_class=ArgumentDefaultsRawHelpFormatter)`
  165. Aliases
  166. ~~~~~~~
  167. The configargparse.ArgumentParser API inherits its class and method
  168. names from argparse and also provides the following shorter names for
  169. convenience:
  170. - p = configargparse.getArgParser() # get global singleton instance
  171. - p = configargparse.getParser()
  172. - p = configargparse.ArgParser() # create a new instance
  173. - p = configargparse.Parser()
  174. - p.add_arg(..)
  175. - p.add(..)
  176. - options = p.parse(..)
  177. HelpFormatters:
  178. - RawFormatter = RawDescriptionHelpFormatter
  179. - DefaultsFormatter = ArgumentDefaultsHelpFormatter
  180. - DefaultsRawFormatter = ArgumentDefaultsRawHelpFormatter
  181. Design Notes
  182. ~~~~~~~~~~~~
  183. Unit tests:
  184. tests/test_configargparse.py contains custom unittests for features
  185. specific to this module (such as config file and env-var support), as
  186. well as a hook to load and run argparse unittests (see the built-in
  187. test.test_argparse module) but on configargparse in place of argparse.
  188. This ensures that configargparse will work as a drop in replacement for
  189. argparse in all usecases.
  190. Previously existing modules (PyPI search keywords: config argparse):
  191. - argparse (built-in module python v2.7+ )
  192. - Good:
  193. - fully featured command line parsing
  194. - can read args from files using an easy to understand mechanism
  195. - Bad:
  196. - syntax for specifying config file path is unusual (eg.
  197. @file.txt)and not described in the user help message.
  198. - default config file syntax doesn't support comments and is
  199. unintuitive (eg. --namevalue)
  200. - no support for environment variables
  201. - ConfArgParse v1.0.15
  202. (https://pypi.python.org/pypi/ConfArgParse)
  203. - Good:
  204. - extends argparse with support for config files parsed by
  205. ConfigParser
  206. - clear documentation in README
  207. - Bad:
  208. - config file values are processed using
  209. ArgumentParser.set_defaults(..) which means "required" and
  210. "choices" are not handled as expected. For example, if you
  211. specify a required value in a config file, you still have to
  212. specify it again on the command line.
  213. - doesn't work with python 3 yet
  214. - no unit tests, code not well documented
  215. - appsettings v0.5 (https://pypi.python.org/pypi/appsettings)
  216. - Good:
  217. - supports config file (yaml format) and env_var parsing
  218. - supports config-file-only setting for specifying lists and
  219. dicts
  220. - Bad:
  221. - passes in config file and env settings via parse_args
  222. namespace param
  223. - tests not finished and don't work with python3 (import
  224. StringIO)
  225. - argparse_config v0.5.1
  226. (https://pypi.python.org/pypi/argparse_config)
  227. - Good:
  228. - similar features to ConfArgParse v1.0.15
  229. - Bad:
  230. - doesn't work with python3 (error during pip install)
  231. - yconf v0.3.2 - (https://pypi.python.org/pypi/yconf) - features
  232. and interface not that great
  233. - hieropt v0.3 - (https://pypi.python.org/pypi/hieropt) - doesn't
  234. appear to be maintained, couldn't find documentation
  235. - configurati v0.2.3 - (https://pypi.python.org/pypi/configurati)
  236. - Good:
  237. - JSON, YAML, or Python configuration files
  238. - handles rich data structures such as dictionaries
  239. - can group configuration names into sections (like .ini files)
  240. - Bad:
  241. - doesn't work with python3
  242. - 2+ years since last release to PyPI
  243. - apparently unmaintained
  244. Design choices:
  245. 1. all options must be settable via command line. Having options that
  246. can only be set using config files or env. vars adds complexity to
  247. the API, and is not a useful enough feature since the developer can
  248. split up options into sections and call a section "config file keys",
  249. with command line args that are just "--" plus the config key.
  250. 2. config file and env. var settings should be processed by appending
  251. them to the command line (another benefit of #1). This is an
  252. easy-to-implement solution and implicitly takes care of checking that
  253. all "required" args are provied, etc., plus the behavior should be
  254. easy for users to understand.
  255. 3. configargparse shouldn't override argparse's
  256. convert_arg_line_to_args method so that all argparse unit tests
  257. can be run on configargparse.
  258. 4. in terms of what to allow for config file keys, the "dest" value of
  259. an option can't serve as a valid config key because many options can
  260. have the same dest. Instead, since multiple options can't use the
  261. same long arg (eg. "--long-arg-x"), let the config key be either
  262. "--long-arg-x" or "long-arg-x". This means the developer can allow
  263. only a subset of the command-line args to be specified via config
  264. file (eg. short args like -x would be excluded). Also, that way
  265. config keys are automatically documented whenever the command line
  266. args are documented in the help message.
  267. 5. don't force users to put config file settings in the right .ini
  268. [sections]. This doesn't have a clear benefit since all options are
  269. command-line settable, and so have a globally unique key anyway.
  270. Enforcing sections just makes things harder for the user and adds
  271. complexity to the implementation.
  272. 6. if necessary, config-file-only args can be added later by
  273. implementing a separate add method and using the namespace arg as in
  274. appsettings_v0.5
  275. Relevant sites:
  276. - http://stackoverflow.com/questions/6133517/parse-config-file-environment-and-command-line-arguments-to-get-a-single-coll
  277. - http://tricksntweaks.blogspot.com/2013_05_01_archive.html
  278. - http://www.youtube.com/watch?v=vvCwqHgZJc8#t=35
  279. .. |Travis CI Status for bw2/ConfigArgParse| image:: https://travis-ci.org/bw2/ConfigArgParse.svg?branch=master