Skip to content

Instantly share code, notes, and snippets.

@bebosudo
Created November 11, 2022 07:00
Show Gist options
  • Save bebosudo/8352d6ba4c8911528f085890becc01c4 to your computer and use it in GitHub Desktop.
Save bebosudo/8352d6ba4c8911528f085890becc01c4 to your computer and use it in GitHub Desktop.
A trick to support environment variables as a backup in Python's argparse when command line argument is not provided
#!/usr/bin/env python3
import argparse
import getpass
import logging
import os
# Check for values inside the environment variables and, if any, set them in the 'default'
# parameter of argparse, so that we don't duplicate code. Just make sure not to use '%(default)s'
# inside the help method, or it would print the env var and not the actual default, if an env var
# was defined.
# Use a **kwargs dict expansion so that we can expand both {"default": os.getenv("num")} into
# default=123 or {} into no default value at all, since passing default=None is not the same as
# not passing any default.
pos_var1_env = "EXPECTED_NAME_OF_POSITIONAL_VAR"
opt_var1_env = "EXPECTED_NAME_OF_FIRST_OPTIONAL_VAR"
opt_var2_env = "EXPECTED_NAME_OF_SECOND_OPTIONAL_VAR"
opt_var1_default = "ajeje"
def get_dict_with_default(env_var):
return {"default": os.getenv(env_var)} if os.getenv(env_var) else {}
parser = argparse.ArgumentParser(description="Here goes a description")
# nargs=? is mandatory for positional arguments; make sure to check after parsing if a
# positional argument was set by an environment variable. Without nargs=? argparse will
# complain because it doesn't find a required positional argument
parser.add_argument("host", nargs="?", **get_dict_with_default(pos_var1_env),
help=(f"Help text for a required argument (if no value provided at cmd
f"line it can also be provided through environment "
f"variable: {pos_var1_env})")
# No need to call the special function above and expand a dictionary, since here we have a
# default value, so default can always be used.
# Make sure not to use %(default)s inside the help text, or that would print the
parser.add_argument("-u", "--username", default=os.getenv(opt_var1_env, opt_var1_default),
help=(f"Help text for optional argument with a default (if no value "
f"from cmd line check in env var: {opt_var1_env}, otherwise "
f"set default value to: {opt_var1_default})"))
parser.add_argument("-p", "--password", **get_dict_with_default(opt_var2_env),
help=(f"Help text for another optional argument with no default (if no "
f"value from cmd line check in env var: {opt_var2_env})"))
args = parser.parse_args()
# Final checks, since nargs=? means that argparse won't require a value at command line, but
# it may get set anyway if it was available as environment variable.
if not args.host:
parser.error(f"Host value not provided at command line nor through environment "
f"variable '{pos_var1_env}'")
# Optional arguments are by default nargs=?, so content must be checked if no default was
# provided.
if not args.password:
# You may want to end up asking the user to type in the password.
prompt = f"Password for user '{args.username}'"
logging.warning(f"{prompt} not provided at command line nor through environment "
f"variable '{opt_var2_env}'")
args.password = getpass.getpass(f"{prompt}: ")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment