Source code for kstlib.utils.dict
"""Dictionary utilities."""
from __future__ import annotations
__all__ = ["deep_merge"]
import copy
from collections.abc import Mapping
from typing import Any
#: Maximum recursion depth for deep_merge to prevent stack overflow on
#: pathological input (e.g. deeply nested YAML from untrusted sources).
MAX_MERGE_DEPTH = 32
[docs]
def deep_merge(
base: dict[str, Any],
updates: Mapping[str, Any],
*,
deep_copy: bool = False,
_depth: int = 0,
) -> dict[str, Any]:
"""Recursively merge updates into base dictionary (in place).
Args:
base: Base dictionary to update (modified in place).
updates: Dictionary with updates to merge.
deep_copy: If True, deep copy values before assignment.
_depth: Internal recursion counter (do not set manually).
Returns:
The modified base dictionary (for chaining).
Raises:
RecursionError: If nesting exceeds MAX_MERGE_DEPTH.
Examples:
>>> base = {"a": {"x": 1}, "b": 2}
>>> deep_merge(base, {"a": {"y": 2}, "c": 3})
{'a': {'x': 1, 'y': 2}, 'b': 2, 'c': 3}
"""
if _depth > MAX_MERGE_DEPTH:
raise RecursionError(f"deep_merge exceeded maximum depth ({MAX_MERGE_DEPTH})")
for key, value in updates.items():
if key in base and isinstance(base[key], dict) and isinstance(value, Mapping):
deep_merge(base[key], value, deep_copy=deep_copy, _depth=_depth + 1)
else:
base[key] = copy.deepcopy(value) if deep_copy else value
return base