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", )