Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting options from environment variables when using argparse

I have a script which has certain options that can either be passed on the command line, or from environment variables. The CLI should take precedence if both are present, and an error occur if neither are set.

I could check that the option is assigned after parsing, but I prefer to let argparse to do the heavy lifting and be responsible for displaying the usage statement if parsing fails.

I have come up with a couple of alternative approaches to this (which I will post below as answers so they can be discussed separately) but they feel pretty kludgey to me and I think that I am missing something.

Is there an accepted "best" way of doing this?

(Edit to make the desired behaviour clear when both the CLI option and environment variable are unset)

like image 998
Russell Heilling Avatar asked May 11 '12 12:05

Russell Heilling


People also ask

How do you add an optional argument in Argparse?

To add an optional argument, simply omit the required parameter in add_argument() . args = parser. parse_args()if args.

What does Nargs do in Argparse?

Number of Arguments If you want your parameters to accept a list of items you can specify nargs=n for how many arguments to accept. Note, if you set nargs=1 , it will return as a list not a single value.

What does Store_true mean?

2. I would add this: store_true means if true was specified then set param value but otherwise leave it to None. If default was also specified then param is set to that value instead of leaving it to None.


2 Answers

You can set the default= of the argument to a .get() of os.environ with the environment variable you want to grab.

You can also pass a 2nd argument in the .get() call, which is the default value if .get() doesn't find an environment variable by that name (by default .get() returns None in that case).

import argparse import os  parser = argparse.ArgumentParser(description='test') parser.add_argument('--url', default=os.environ.get('URL'))  args = parser.parse_args() if not args.url:     exit(parser.print_usage()) 
like image 74
Christian Witts Avatar answered Sep 30 '22 18:09

Christian Witts


I use this pattern frequently enough that I have packaged a simple action class to handle it:

import argparse import os  class EnvDefault(argparse.Action):     def __init__(self, envvar, required=True, default=None, **kwargs):         if not default and envvar:             if envvar in os.environ:                 default = os.environ[envvar]         if required and default:             required = False         super(EnvDefault, self).__init__(default=default, required=required,                                           **kwargs)      def __call__(self, parser, namespace, values, option_string=None):         setattr(namespace, self.dest, values) 

I can then call this from my code with:

import argparse from envdefault import EnvDefault  parser=argparse.ArgumentParser() parser.add_argument(     "-u", "--url", action=EnvDefault, envvar='URL',      help="Specify the URL to process (can also be specified using URL environment variable)") args=parser.parse_args() 
like image 25
Russell Heilling Avatar answered Sep 30 '22 18:09

Russell Heilling