You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bazarr/libs/dynaconf/loaders/env_loader.py

134 lines
3.9 KiB

from __future__ import annotations
from contextlib import suppress
from os import environ
from dynaconf.loaders.base import SourceMetadata
from dynaconf.utils import missing
from dynaconf.utils import upperfy
from dynaconf.utils.parse_conf import boolean_fix
from dynaconf.utils.parse_conf import parse_conf_data
DOTENV_IMPORTED = False
with suppress(ImportError, FileNotFoundError):
from dynaconf.vendor.dotenv import cli as dotenv_cli
DOTENV_IMPORTED = True
IDENTIFIER = "env"
def load(obj, env=None, silent=True, key=None, validate=False):
"""Loads envvars with prefixes:
`DYNACONF_` (default global) or `$(ENVVAR_PREFIX_FOR_DYNACONF)_`
"""
global_prefix = obj.get("ENVVAR_PREFIX_FOR_DYNACONF")
if global_prefix is False or global_prefix.upper() != "DYNACONF":
load_from_env(
obj,
"DYNACONF",
key,
silent,
IDENTIFIER + "_global",
validate=validate,
)
# Load the global env if exists and overwrite everything
load_from_env(
obj,
global_prefix,
key,
silent,
IDENTIFIER + "_global",
validate=validate,
)
def load_from_env(
obj,
prefix=False,
key=None,
silent=False,
identifier=IDENTIFIER,
env=False, # backwards compatibility bc renamed param
validate=False,
):
if prefix is False and env is not False:
prefix = env
env_ = ""
if prefix is not False:
if not isinstance(prefix, str):
raise TypeError("`prefix/env` must be str or False")
prefix = prefix.upper()
env_ = f"{prefix}_"
# set source metadata
source_metadata = SourceMetadata(identifier, "unique", "global")
# Load a single environment variable explicitly.
if key:
key = upperfy(key)
value = environ.get(f"{env_}{key}")
if value:
try: # obj is a Settings
obj.set(
key,
boolean_fix(value),
loader_identifier=source_metadata,
tomlfy=True,
validate=validate,
)
except AttributeError: # obj is a dict
obj[key] = parse_conf_data(
boolean_fix(value), tomlfy=True, box_settings=obj
)
# Load environment variables in bulk (when matching).
else:
# Only known variables should be loaded from environment?
ignore_unknown = obj.get("IGNORE_UNKNOWN_ENVVARS_FOR_DYNACONF")
# prepare data
trim_len = len(env_)
data = {
key[trim_len:]: parse_conf_data(
boolean_fix(value), tomlfy=True, box_settings=obj
)
for key, value in environ.items()
if key.startswith(env_)
and not (
# Ignore environment variables that haven't been
# pre-defined in settings space.
ignore_unknown
and obj.get(key[trim_len:], default=missing) is missing
)
}
# Update the settings space based on gathered data from environment.
if data:
filter_strategy = obj.get("FILTER_STRATEGY")
if filter_strategy:
data = filter_strategy(data)
obj.update(
data, loader_identifier=source_metadata, validate=validate
)
def write(settings_path, settings_data, **kwargs):
"""Write data to .env file"""
if not DOTENV_IMPORTED: # pragma: no cover
return
for key, value in settings_data.items():
quote_mode = (
isinstance(value, str)
and (value.startswith("'") or value.startswith('"'))
) or isinstance(value, (list, dict))
dotenv_cli.set_key(
str(settings_path),
key,
str(value),
quote_mode="always" if quote_mode else "none",
)