I wish to use the argparse
module to set the value of a server URL. As well as being set via an argument, the vale can also be in an environment variable, though the argument should always take precedence.
To achieve this, I've added an argument to the parser with the environment variable value as the default, and a custom type. However, if the argument is omitted, the custom type=EnvironDefault
is not initialised. This means that if default value is None
, that value is used regardless, rather than an exception being raised as expected.
I have found a 10 year old question that suggests it is necessary to check the value of the argument after parsing the arguments, but I'm hoping that in the interim a solution has been added to argparse
.
foo@bar:~$ export SERVER_URL="https://some.server.co.uk"
foo@bar:~$ python mycli
Namespace(url='https://some.server.co.uk')
foo@bar:~$ export SERVER_URL="https://some.server.co.uk"
foo@bar:~$ python mycli --url "https://different.server.co.uk"
Namespace(url='https://different.server.co.uk')
foo@bar:~$ unset SERVER_URL
foo@bar:~$ python mycli
Namespace(url=None)
usage: mycli [-h] [--url URL]
mycli: error: argument --url: Server URL must be provided via either the CLI or the environment variable SERVER_URL.
import argparse
import os
class EnvironDefault(str):
def __init__(self, url: str) -> None:
err_msg = (
'Server URL must be provided via either the CLI or the '
'environment variable SERVER_URL.')
if not url:
raise argparse.ArgumentTypeError(err_msg)
parser = argparse.ArgumentParser()
parser.add_argument(
'--url', type=EnvironDefault, default=os.environ.get('SERVER_URL'),
help='Server URL. Overrides environment variable SERVER_URL.')
print(parser.parse_args())
When the program starts, you can check immediately if SERVER_URL
is available, and use that fact to decide if --url
is required or not.
parser = argparse.ArgumentParser()
parser.add_argument(
'--url', required='SERVER_URL' not in os.environ, default=os.environ.get('SERVER_URL'),
help='Server URL. Overrides environment variable SERVER_URL.')
print(parser.parse_args())
If SERVER_URL
is not in the environment, the default value of None
will be ignored, because --url
is required. If it is in the environment, then the default value will be used.
Note that default values are not processed by your type. If you do use a custom type, it should be applied to the default explicitly. For example -
parser.add_argument('--value',
required='VALUE' not in os.environ,
type=int,
default=int(os.environ.get('VALUE')))