Source code for dump_env.dumper
from collections import OrderedDict
from os import environ
from typing import Dict, Final, List, Mapping, Optional, Set
from dump_env.exceptions import StrictEnvError
Store = Mapping[str, str]
EMPTY_STRING: Final = ''
def _parse(source: str) -> Store:
"""
Reads the source ``.env`` file and load key-values.
Args:
source: ``.env`` template filepath
Returns:
Store with all keys and values.
"""
parsed_data = {}
with open(source) as env_file:
for line in env_file:
line = line.strip() # noqa: WPS440
if not line or line.startswith('#') or '=' not in line:
# Ignore comments and lines without assignment.
continue
# Remove whitespaces and quotes:
env_name, env_value = line.split('=', 1)
env_name = env_name.strip()
env_value = env_value.strip().strip('\'"')
parsed_data[env_name] = env_value
return parsed_data
def _preload_existing_vars(prefix: str) -> Store:
"""Preloads env vars from environ with the given prefix."""
if not prefix:
# If prefix is empty just return all the env variables.
return environ
prefixed = {}
# Prefix is not empty, do the search and replacement:
for env_name, env_value in environ.items():
if not env_name.startswith(prefix):
# Skip vars with no prefix.
continue
prefixed[env_name.replace(prefix, '', 1)] = env_value
return prefixed
def _preload_specific_vars(env_keys: Set[str]) -> Store:
"""Preloads env vars from environ in the given set."""
specified = {}
for env_name, env_value in environ.items():
if env_name not in env_keys:
# Skip vars that have not been requested.
continue
specified[env_name] = env_value
return specified
def _assert_envs_exist(strict_keys: Set[str]) -> None:
"""Checks that all variables from strict keys do exists."""
missing_keys: List[str] = [
strict_key
for strict_key in strict_keys
if strict_key not in environ
]
if missing_keys:
raise StrictEnvError(
'Missing env vars: {0}'.format(', '.join(missing_keys)),
)
def _source(source: str, strict_source: bool) -> Store:
"""Applies vars and assertions from source template ``.env`` file."""
sourced: Dict[str, str] = {}
sourced.update(_parse(source))
if strict_source:
_assert_envs_exist(set(sourced.keys()))
sourced.update(_preload_specific_vars(set(sourced.keys())))
return sourced
[docs]
def dump(
template: str = EMPTY_STRING,
prefixes: Optional[List[str]] = None,
strict_keys: Optional[Set[str]] = None,
source: str = EMPTY_STRING,
strict_source: bool = False,
) -> Dict[str, str]:
"""
This function is used to dump ``.env`` files.
As a source you can use both:
1. env.template file (``''`` by default)
2. env vars prefixed with some prefix (``''`` by default)
Args:
template: The path of the ``.env`` template file,
use an empty string when there is no template file.
prefixes: List of string prefixes to use only certain env
variables, could be an empty string to use all available variables.
strict_keys: List of keys that must be presented in env vars.
source: The path of the ``.env`` template file,
defines the base list of env vars that should be checked,
disables the fetching of non-prefixed env vars,
use an empty string when there is no source file.
strict_source: Whether all keys in source template must also be
presented in env vars.
Returns:
Ordered key-value pairs of dumped env and template variables.
Raises:
StrictEnvError: when some variable from template is missing.
"""
if prefixes is None:
prefixes = [] if source else [EMPTY_STRING]
if strict_keys:
_assert_envs_exist(strict_keys)
store: Dict[str, str] = {}
if source:
# Loading env values from source template file:
store.update(_source(source, strict_source))
if template:
# Loading env values from template file:
store.update(_parse(template))
# Loading env variables from `os.environ`:
for prefix in prefixes:
store.update(_preload_existing_vars(prefix))
# Sort keys and keep them ordered:
return OrderedDict(sorted(store.items()))