134 lines
4.8 KiB
Python
134 lines
4.8 KiB
Python
import argparse
|
|
import os
|
|
from functools import partial
|
|
from pathlib import Path
|
|
from typing import Union
|
|
|
|
from dynaconf import Dynaconf, ValidationError, Validator
|
|
from funcy import lflatten
|
|
from kn_utils.logging import logger
|
|
|
|
# This path is ment for testing purposes and convenience. It probably won't reflect the actual root path when pyinfra is
|
|
# installed as a package, so don't use it in production code, but define your own root path as described in load config.
|
|
local_pyinfra_root_path = Path(__file__).parents[2]
|
|
|
|
|
|
def load_settings(
|
|
settings_path: Union[str, Path, list] = "config/",
|
|
root_path: Union[str, Path] = None,
|
|
validators: list[Validator] = None,
|
|
):
|
|
"""Load settings from .toml files, .env and environment variables. Also ensures a ROOT_PATH environment variable is
|
|
set. If ROOT_PATH is not set and no root_path argument is passed, the current working directory is used as root.
|
|
Settings paths can be a single .toml file, a folder containing .toml files or a list of .toml files and folders.
|
|
If a ROOT_PATH environment variable is set, it is not overwritten by the root_path argument.
|
|
If a folder is passed, all .toml files in the folder are loaded. If settings path is None, only .env and
|
|
environment variables are loaded. If settings_path are relative paths, they are joined with the root_path argument.
|
|
"""
|
|
|
|
root_path = get_or_set_root_path(root_path)
|
|
validators = validators or get_pyinfra_validators()
|
|
|
|
settings_files = normalize_to_settings_files(settings_path, root_path)
|
|
|
|
settings = Dynaconf(
|
|
load_dotenv=True,
|
|
envvar_prefix=False,
|
|
settings_files=settings_files,
|
|
)
|
|
|
|
validate_settings(settings, validators)
|
|
logger.info("Settings loaded and validated.")
|
|
|
|
return settings
|
|
|
|
|
|
def normalize_to_settings_files(settings_path: Union[str, Path, list], root_path: Union[str, Path]):
|
|
if settings_path is None:
|
|
logger.info("No settings path specified, only loading .env end ENVs.")
|
|
settings_files = []
|
|
elif isinstance(settings_path, str) or isinstance(settings_path, Path):
|
|
settings_files = [settings_path]
|
|
elif isinstance(settings_path, list):
|
|
settings_files = settings_path
|
|
else:
|
|
raise ValueError(f"Invalid settings path: {settings_path=}")
|
|
|
|
settings_files = lflatten(map(partial(_normalize_and_verify, root_path=root_path), settings_files))
|
|
logger.debug(f"Normalized settings files: {settings_files}")
|
|
|
|
return settings_files
|
|
|
|
|
|
def _normalize_and_verify(settings_path: Path, root_path: Path):
|
|
settings_path = Path(settings_path)
|
|
root_path = Path(root_path)
|
|
|
|
if not settings_path.is_absolute():
|
|
logger.debug(f"Settings path is not absolute, joining with root path: {root_path}")
|
|
settings_path = root_path / settings_path
|
|
|
|
if settings_path.is_dir():
|
|
logger.debug(f"Settings path is a directory, loading all .toml files in the directory: {settings_path}")
|
|
settings_files = list(settings_path.glob("*.toml"))
|
|
elif settings_path.is_file():
|
|
logger.debug(f"Settings path is a file, loading specified file: {settings_path}")
|
|
settings_files = [settings_path]
|
|
else:
|
|
raise ValueError(f"Invalid settings path: {settings_path=}, {root_path=}")
|
|
|
|
return settings_files
|
|
|
|
|
|
def get_or_set_root_path(root_path: Union[str, Path] = None):
|
|
env_root_path = os.environ.get("ROOT_PATH")
|
|
|
|
if env_root_path:
|
|
root_path = env_root_path
|
|
logger.debug(f"'ROOT_PATH' environment variable is set to {root_path}.")
|
|
|
|
elif root_path:
|
|
logger.info(f"'ROOT_PATH' environment variable is not set, setting to {root_path}.")
|
|
os.environ["ROOT_PATH"] = str(root_path)
|
|
|
|
else:
|
|
root_path = Path.cwd()
|
|
logger.info(f"'ROOT_PATH' environment variable is not set, defaulting to working directory {root_path}.")
|
|
os.environ["ROOT_PATH"] = str(root_path)
|
|
|
|
return root_path
|
|
|
|
|
|
def get_pyinfra_validators():
|
|
import pyinfra.config.validators
|
|
|
|
return lflatten(
|
|
validator for validator in pyinfra.config.validators.__dict__.values() if isinstance(validator, list)
|
|
)
|
|
|
|
|
|
def validate_settings(settings: Dynaconf, validators):
|
|
settings_valid = True
|
|
|
|
for validator in validators:
|
|
try:
|
|
validator.validate(settings)
|
|
except ValidationError as e:
|
|
settings_valid = False
|
|
logger.warning(e)
|
|
|
|
if not settings_valid:
|
|
raise ValidationError("Settings validation failed.")
|
|
|
|
logger.debug("Settings validated.")
|
|
|
|
|
|
def parse_settings_path():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"settings_path",
|
|
help="Path to settings file(s) or folder(s). Must be .toml file(s) or a folder(s) containing .toml files.",
|
|
nargs="+",
|
|
)
|
|
return parser.parse_args().settings_path
|