Configuration via environment variables has become popular with the rise of
twelve-factor apps, yet few Python libraries exist to help with it (despite
the abundance of libraries for command line and file configuration).

When I looked around, most of the solutions I found involved using
os.environ directly, or overloading somewhat related libraries such
as argparse or formencode. The former are not robust enough
with regards to typecasting and error handling. The latter are inappropriate
and overengineered: the reason to prefer envvar configuration in the first
place is to reduce complexity, not compound it. We need something designed
specifically and solely for taking configuration from environment variables.

The one library I found is python-decouple, which does indeed rationalize
typecasting of environment variables. However, it also handles file
configuration, which adds unwanted complexity and (ironically) muddying of
concerns. Additionally, it doesn’t enable robust error messaging. The problem
with error handling in decouple and in ad-hoc usage of
os.environ is that if you have four environment variables wrong, you
only find out about them one at a time. We want to find out about all problems
with our configuration at once, so that we can solve them all at once instead
of playing configuration roulette (“Will it work this time? No! How about
now?”).

This present library is designed to be small in scope, limited to environment
variables only, and to support robust error messaging. Look into foreman and
honcho for process management tools to complement this library.

Keyword arguments specify which variables to look for and how to typecast them.
Since a process environment contains a lot of crap you don’t care about, we
only parse out variables that you explicitly specify in the keyword arguments.

The resulting object has lowercase attributes for all variables that were asked
for and found:

>>> env.foo42

There are also missing and
malformed attributes for variables that weren’t found
or couldn’t be typecast:

You’re expected to inspect the contents of missing and
malformed and do your own error reporting. You’re also
expected to handle defaults yourself at a higher level—this is not a
general-purpose configuration library—though the
parsed dictionary should help with that:

>>> env.parsed{'foo': 42}

If all of the environment variables you care about share a common prefix, you
can specify this to the constructor to save yourself some clutter:

prefix (string) – If all of the environment variables of interest to
you share a common prefix, you can specify that here. We will use this
prefix when pulling values out of the environment, and the attribute
names you end up with won’t include the prefix.

environ (mapping) – By default we look at os.environ, of
course, but you can override that with this. We operate on a shallow
copy of this mapping (though it’s effectively a deep copy in the normal
case where all values are strings, since strings are immutable).

kw – Keyword arguments are folded into spec.

The constructor for this class loops through the items in environ,
skipping those variables not also named in spec, and parsing those that
are, using the type specified. Under Python 2, we harmonize with Python
3’s behavior by decoding environment variable values to unicode
using the result of sys.getfilesystemencoding before
typecasting. The upshot is that if you want typecasting to be a
pass-through for a particular variable, you should specify the
Python-version-appropriate string type: str for Python 3,
unicode for Python 2. We store variables using lowercased
names, so MYVAR would end up at env.myvar:

>>> env=Environment(MYVAR=int,environ={'MYVAR':42})>>> env.myvar42

If a variable is mentioned in spec but is not in environ, the
variable name is recorded in the missing list. If typecasting a
variable raises an exception, the variable name and an error message are
recorded in the malformed list:

Use the parsed dictionary, for example, to fold
configuration from the environment together with configuration from other
sources (command line, config files, defaults) in higher-order data
structures. Attribute access for non-class attributes on
Environment instances uses parsed
rather than __dict__, which means that you can set attributes on
the instance and they’re reflected in parsed: