Source code for behave_kit.env.profiles
"""Profile selection and override application for raw `behave.toml` data.
Operates on plain dicts (as returned by `tomllib.load`) so profile
resolution can be unit-tested without touching the filesystem or
`KitConfig`.
"""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from behave_kit._core.errors import ConfigError
def _merge_profiles(default: Mapping[str, Any], profile: Mapping[str, Any]) -> dict[str, Any]:
"""Merge ``profile`` over ``default`` recursively for nested dicts."""
merged: dict[str, Any] = dict(default)
for key, value in profile.items():
if key in merged and isinstance(merged[key], Mapping) and isinstance(value, Mapping):
merged[key] = _merge_profiles(merged[key], value)
else:
merged[key] = value
return merged
[docs]
def select_profile(toml_data: dict[str, Any], env_name: str) -> dict[str, Any]:
"""Return the ``[env.<env_name>]`` section merged over ``[env.default]``.
Raises:
ConfigError: if ``env_name`` has no matching section or the TOML
structure is invalid.
"""
if not isinstance(toml_data, Mapping):
raise ConfigError("Configuration data must be a mapping")
environments = toml_data.get("env", {})
if not isinstance(environments, Mapping):
raise ConfigError("'env' must be a table")
default_section = environments.get("default", {})
if not isinstance(default_section, Mapping):
raise ConfigError("'env.default' must be a table")
profile = environments.get(env_name)
if not isinstance(profile, Mapping):
raise ConfigError(
f"Environment profile 'env.{env_name}' not found",
suggestion=f"Available profiles: {', '.join(sorted(environments)) or '(none)'}",
)
return _merge_profiles(default_section, profile)
[docs]
def apply_overrides(config_dict: dict[str, Any], overrides: dict[str, str]) -> dict[str, Any]:
"""Return ``config_dict`` with CLI ``--set key=value`` overrides applied on top."""
merged = dict(config_dict)
merged.update(overrides)
return merged