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.
134 lines
3.9 KiB
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",
|
|
)
|