Allow loading of environment variables into the config

This new method will pick out any environment variables with a certain
prefix and place them into the config named without the prefix. This
makes it easy to use environment variables to configure the app as is
now more popular than when Flask started.

The prefix should ensure that the environment isn't polluted and the
config isn't polluted by environment variables.

I've followed the dynaconf convention of trying to parse the
environment variable and then falling back to the raw value if parsing
fails.
This commit is contained in:
pgjones 2022-03-08 21:40:48 +00:00 committed by David Lord
parent 425a62686f
commit 08a283af5e
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
3 changed files with 94 additions and 23 deletions

View file

@ -1,4 +1,5 @@
import errno
import json
import os
import types
import typing as t
@ -6,6 +7,13 @@ import typing as t
from werkzeug.utils import import_string
def _json_loads(raw: t.Union[str, bytes]) -> t.Any:
try:
return json.loads(raw)
except json.JSONDecodeError:
return raw
class ConfigAttribute:
"""Makes an attribute forward to the config"""
@ -97,6 +105,44 @@ class Config(dict):
)
return self.from_pyfile(rv, silent=silent)
def from_prefixed_env(
self,
prefix: str = "FLASK_",
*,
loads: t.Callable[[t.Union[str, bytes]], t.Any] = _json_loads,
) -> bool:
"""Updates the config from environment variables with the prefix.
Calling this method will result in every environment variable
starting with **prefix** being placed into the configuration
without the **prefix**. The prefix is configurable as an
argument. Note that this method updates the existing config.
For example if there is an environment variable
``FLASK_SECRET_KEY`` with value ``secretly`` and the prefix is
``FLASK_`` the config will contain the key ``SECRET_KEY`` with
the value ``secretly`` after calling this method.
The value of the environment variable will be passed to the
**loads** parameter before being placed into the config. By
default **loads** utilises the stdlib json.loads to parse the
value, falling back to the value itself on parsing error.
:param loads: A callable that takes a str (or bytes) returns
the parsed value.
:return: Always returns ``True``.
.. versionadded:: 2.1.0
"""
mapping = {}
for raw_key, value in os.environ.items():
if raw_key.startswith(prefix):
key = raw_key[len(prefix) :] # Use removeprefix with Python 3.9
mapping[key] = loads(value)
return self.from_mapping(mapping)
def from_pyfile(self, filename: str, silent: bool = False) -> bool:
"""Updates the values in the config from a Python file. This function
behaves as if the file was imported as module with the