Package: fern
You want to be all12factorand read your config from the environ.You also deploy your project in a variety of environments (dev, staging,
prod…) and you want to be able to build in sane defaults for some of those
environments (dev especially) so you don’t have to carry around huge
boilerplate.envfiles that aren’t in version control. But you don’t want
to risk those built-in dev defaults silently getting used in production.Fern’s got your back.In the simplest case, afern.Envinstance lets you call it to read strings
from the environ:>>> env = fern.Env()
>>> env('FOO')
'bar'If we tried that and theFOOenv var were not set, we’d get aValueErrorinstead. Maybe that’s OK, if this is a critical config value
that should always be set explicitly in the environ. If it’s less critical, we
can set a default:>>> env = fern.Env()
>>> env('DOES_NOT_EXIST', default='hey')
'hey'We can also give a list of names of env vars, and fern will check each one and
give us the first value that’s set:>>> env = fern.Env()
>>> env(['DOES_NOT_EXIST', 'DOES_EXIST'])
'value_of_DOES_EXIST'All environment values are strings. What if we want to parse this string into a
more structured data type? We can pass any unary coercion function to be
applied to the value; e.g. theinttype itself works as a unary coercion
function: passed one string, it’ll return that string parsed as an integer:>>> env = fern.Env()
>>> env('SOME_INT', coerce=int)
6You can write any function you like that takes in a string and returns
whatever, and pass it tocoerce. For instance,fern.parse_dj_database_urlwill parse a database URL like'postgres://localhost/dname'and return a Django-style database connection
info dictionary.TheEnvclass has a few convenience methods for certain common values ofcoerce:>>> env = fern.Env()
>>> env.integer('SOME_INT')
6
>>> env.boolean('SOME_BOOL')
True
>>> env.comma_list('SOME_LIST')
['a', 'b', 'c']Theintegermethod just setsintas the coercion function.Thebooleanmethod setsfern.parse_booleanas the coercion function; it
considers the empty string,'0','no','f','n'and'false'to beFalse; anything else isTrue.Thecomma_listmethod setsfern.parse_comma_listas the coercion
function; it splits the env value on commas and returns the resulting values as
a list.Now let’s make things a bit more complex. Let’s say we want two deployment
modes,devandprod, and we’ve got a config valueSECRET_KEY. In
dev mode, we want this value to default to"dev secret"(but still be
overridable via theSECRET_KEYenv var). In prod mode, we want to error out
if theSECRET_KEYenv var is not set explicitly; no hardcoded default could
be safe for production use. We can achieve that like this:>>> env = fern.Env('MODE', valid_modes=['dev', 'prod'])
>>> env('SECRET_KEY', mode_defaults={'dev': "dev secret"})Let’s unpack that a bit.In the first line, we tell Fern that our valid modes aredevorprod,
and that an env var namedMODEwill tell us which mode we are in. (The
default mode is the first one listed, so ifMODEis not set we’ll be indevmode. IfMODEis set to something not listed invalid_modes,
we’ll get aValueError).In the second line, we supply a dictionary for themode_defaultsargument. The keys in this dictionary are mode names, and the values are
defaults to use for that mode. In this case, we supply a default only fordevmode; inprodmode if theSECRET_KEYenv var is not set, you’ll
get aValueError. So for our prod deployments, all we have to make sure to
do is setMODE=prod, and that ensures the server won’t start unless we also
supply the rest of the required config. In dev mode, we don’t need any env vars
at all.CHANGES0.2 (2017.01.08)Fixmode_defaultswith convenience coercion methods.0.1 (2017.01.07)Initial version.