Package: appcfg
appcfgFlexible hierarchic Python application configuration inspired bynode-configMotivationApplications (especially web services) often require certain configuration options to depend on the environment an application runs in (development, testing, production, etc.).
For instance, a database address config option may default to a local database server during development, a mock database server during testing, and yet another database server during production.
It may also need to be customizable via an environment variable.appcfgapproaches scenarios like this and, similar tonode-configfor Node.js, allows to specify default configuration options for various environments and optionally override them by custom environment variables.Getting StartedLet's start by installingappcfgwithpip install appcfg[yaml], or simplypip install appcfgif you want to use the JSON format instead of YAML for config files.In the top-level directory of your application (where the topmost__init__.pyfile is located), create aconfigdirectory.
If your application consists of a single Python file, just locate theconfigdirectory next to it.
Here's an example project tree that we will refer to in the rest of this section:├── myproject│   ├── __init__.py+│   ├── config+|   |   └── ...|   ├── anothermodule.py│   └── myproject.py├── tests│   ├── __init__.py│   └── test_myproject.pyWithin theconfigdirectory, create adefault.ymlfile (ordefault.json, if you prefer that).
This file will hold your default configuration.
In the database example from the previous section,default.ymlmight look like this:databases:mongo:host:localhostport:27017user:myuserpass:mypasswordsecrets:mysecret:secretWithin any module in themyprojectpackage, we can now simply access that configuration as adictobject:fromappcfgimportget_configconfig=get_config(__name__)print(config["databases"]["mongo"]["host"])__name__is passed toget_configso that it can infer the project root path where theconfigdirectory is located.
This way,appcfgcan be used independently in multiple projects loaded at the same time, and projects can also retrieve one another's configuration.
For instance, intest_myproject.py, the configuration ofmyprojectcould be retrieved withget_config("myproject").EnvironmentsLet's add an override config file for production,production.yml:databases:mongo:host:mongodbAnd one for testing,test.yml:databases:mongo:host:mocksecrets:mysecret:wellknownBy default, none of these config files will be used.
However, an environment can be specified by setting theENVenvironment variable (alternatively,PY_ENVorENVIRONMENTare also supported).
In this case, the configuration options from the corresponding config file will be merged into the ones fromdefault.yml.
If, for instance,ENVis set toproduction, the config dict returned fromget_config()will contain all the values fromdefault.yml, butconfig["databases"]["mongo"]["host"]will be set tomongodbinstead oflocalhost.
Similarly, withENV=test,config["databases"]["mongo"]["host"]would bemockand,config["secret"]["mysecret"]would bewellknown.
In caseENVis set but no corresponding config file is found,get_config()returns the options from thedefaultconfig file.Custom Environment VariablesLet's say, we want the database host, user, and password, and the secret to be customizable via additional environment variables.
This can be achieved by adding anenv-vars.ymlconfig file with the following contents:databases:mongo:host:MONGO_HOSTuser:MONGO_USERpass:MONGO_PASSsecrets:mysecret:MY_SECRETThis way, if one of the specified environment variables is set, it will override the corresponding field's value from any other configuration file.
Otherwise, that value is left untouched.
For instance, settingMONGO_HOST=myhostwould result inconfig["databases"]["mongo"]["host"]to bemyhost, ignoringlocalhostfromdefault.ymlormongodbfromproduction.yml.
Note that config values set via environment variables are always of typestr, regardless of the overridden value's type.Tips and TricksEnvironment Specification with pytestYou may wish to setENV=testduring unit tests without manually specifying it for everypytestinvocation.pytest-envcan do the job for you if you specify[pytest]env=ENV=testin your pytest configuration.APIget_configReturns a dict that contains the content ofdefault.jsonordefault.ymlin theconfigdirectory within the root module's directory, inferred frommodule_name.If anENV,PY_ENV, orENVIRONMENTvariable is set (listed in the order of
precedence), and a config file with a base name corresponding to that variable's
value is found, the contents of that config file are merged into the default
configuration. Additionally, the environment variables specified inenv-vars.jsonorenv-vars.ymloverride any other configuration when they are set.If none ofENV,PY_ENV, orENVIRONMENTis set, only thedefaultconfig file
will be loaded and optionally be overridden by custom environment variables as
specified in theenv-varsconfig file. TheENVvalues "dev" and "develop" map to
thedevelopment.jsonordevelopment.ymlconfig file.Arguments:module_name(str): The name of the module (or any of its submodules) for which
the configuration should be loaded.__name__can be used whenget_config()is
called from the Python project that contains theconfigdirectory. Note thatappcfgrequires theconfigdirectory to be a direct child of the top-level
package directory, or, if not a package, the directory that contains the specified
module.cached(bool): IfTrue(the default), the configuration for eachconfigdirectory will only be loaded once and the same dict object will be returned by
every subsequent call toget_config().get_envIf anENV,PY_ENV, orENVIRONMENT(listed in the order of precedence) environment variable is set, return the value of it. Otherwise, return "default".Special cases:"dev" and "develop" are mapped to "development"