envargs
gives you an easy way of specifying which environment variables to
look for as program arguments/config. This library aims at providing an as
similar as possible experience to that of using Python's argparse
package.
pip install envarguments
As most containerized applications take most or at least part of their
runtime config from environment variables I always seemed to reimplement the
same parsing utilities for new projects. envargs.EnvParser
gives you a
no-dependencies quick way of specifying which variables your program needs
to run, which are mandatory and which are not, and their data types. A
single call to parse_env
and your registered environment variables are
decoded, ready to be used.
Registering interesting variables and getting a hold of them in your running program is very straight forward:
import envargs
NUM_WORKERS = "NUM_WORKERS"
RETENTION_POLICY = "RETENTION_POLICY"
RETENTION_POLICY_DEFAULT = "keep-alive"
parser = envargs.EnvParser()
parser.add_variable(NUM_WORKERS, type=int, default=1)
parser.add_variable(RETENTION_POLICY, default=RETENTION_POLICY_DEFAULT)
namespace = parser.parse_env()
num_workers = namespace.num_workers
retention_policy = namespace.retention_policy
...
Register interesting variables with add_variable
and call parse_env()
to
obtain a populated namespace. The namespace will have one property per found
registered variable, named the same as the environment variable in lower
case. Variable NUM_WORKERS
thus gets the field name namespace.num_workers
.
In addition, to further clarify how your
application uses environment variables, an envargs.EnvParser
provides a
helpful description property that can help determine if you have set up your
variables as expected. After calling add_variables
on your EnvParser
instance for each interesting variable, the parsers description
field will
contain a helpful text which describes all added variables, if they're
required, their types, and their default values (if any).
import envargs
import logging
LOGGER = logging.getLogger(__name__)
env_parser = envargs.EnvParser()
env_parser.add_variable("NUM_WORKERS", type=int)
env_parser.add_variable("RETRY_COUNTER", required=False, type=int)
env_parser.add_variable("OVERLOAD_LIMIT", default="high")
LOGGER.debug(env_parser.description)
This text can be added to an argparse.ArgumentParser
description to show
the text easily from the commandline. Note the use of the raw formatter
class to avoid stripping line breaks.
import argparse
import envargs
env_parser = envargs.EnvParser()
env_parser.add_variable("WORK_DIST_STRATEGY", default="round-robin")
env_parser.add_variable("CPU_OVERLOAD_LIMIT_PERCENT", type=int, default=80)
arg_parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""
This is the best program, ever written, clearly.
Below, some info on environment variables used \/
""" + env_parser.description)
...
An environment variable, when registered, can be given a set of properties,
all available as keyword arguments given to EnvParser.add_variable(...)
.
If no keyword arguments are given, just the name of the environment variable
itself, it will be assumed that:
- The variable is required (raising a
RequiredError
in case it's missing) - The variable is a string (meaning no special decoding of the string
gotten from
os.getenv(...)
) is done - There is no default value
Read the docstring on EnvParser.add_variable
to see all available options.
Below is an example of a non-required boolean variable with a default value:
import envargs
def do_throttling(acceptor_throttling: bool):
...
env_parser = envargs.EnvParser()
env_parser.add_variable("ACCEPTOR_THROTTLING", type=bool, required=False,
default=False)
ns = env_parser.parse_env()
do_throttling(ns.acceptor_throttling)
The above will attempt to find the environment variable
ACCEPTOR_THROTTLING
and if it doesn't exist, fall back on False
as a
default value. The namespace returned by parse_env
will therefore always
be populated with the acceptor_throttling
property.
An optional environment variable that isn't present at the time of calling
parse_env
will still yield an attribute in the returmed namespace, set to
None
.
import envargs
env_parser = envargs.EnvParser()
env_parser.add_variable("OPTIONAL_SETTING", required=False)
namespace = env_parser.parse_env()
# Will not raise an assertion error
assert namespace.optional_setting is None
To simplify things for this library, booleans can be expressed in only a few selected ways:
TRUTH_SET = {"true", "True", "1"}
FALSE_SET = {"false", "False", "0"}
These definitions are available at envargs.envargs.TRUTH_SET/FALSE_SET
.
Deviating from these values for a boolean variable will result in a
ParseError
:
import envargs
import os
from envargs.errors import ParseError
SYSTEM_ONLINE = "SYSTEM_ONLINE"
# Set the env var to something other than what's defined in the
# TRUTH_SET/FALSE_SET
os.environ[SYSTEM_ONLINE] = "YES"
env_parser = envargs.EnvParser()
env_parser.add_variable(SYSTEM_ONLINE, type=bool)
# This statement raises a ParseError
try:
env_parser.parse_env()
except ParseError as e:
print(e)