inherit

Hierarchical Configuration Inheritance Pattern

This module provides a DRY (Don’t Repeat Yourself) solution for configuration management by implementing inheritance patterns similar to object-oriented programming, but for JSON-like data structures.

Problem It Solves

When managing configurations for multiple environments (dev, staging, prod), you often need to repeat common settings across environments. This leads to duplication and maintenance overhead.

Solution

Use a special _defaults section to define default values that automatically inherit to other sections, while allowing environment-specific overrides.

How It Works

The _defaults section contains JSON path patterns that specify where default values should be applied. Values are only set if they don’t already exist (no overwriting).

Basic Example

Input configuration:

{
    "_defaults": {
        "*.username": "root",       # Apply to all environments
        "*.memory": 2               # Default memory allocation
    },
    "dev": {
        "password": "dev123"        # Dev-specific setting
    },
    "prod": {
        "password": "prod456",      # Prod-specific setting
        "memory": 8                 # Override default memory
    }
}

After applying inheritance, becomes:

{
    "dev": {
        "username": "root",         # Inherited from _defaults
        "password": "dev123",       # Original value
        "memory": 2                 # Inherited from _defaults
    },
    "prod": {
        "username": "root",         # Inherited from _defaults
        "password": "prod456",      # Original value
        "memory": 8                 # Override (not replaced)
    }
}

JSON Path Patterns

  • *.field: Apply to all top-level keys (except _defaults)

  • env.field: Apply to specific environment

  • *.db.*.port: Apply to nested structures with wildcards

  • env.services.port: Apply to specific nested path

Key Features

  • Non-destructive: Existing values are never overwritten

  • Recursive: Supports nested _defaults sections for fine-grained control

  • Flexible: Works with dictionaries and lists of dictionaries

  • Order-aware: Evaluation order matters for overlapping patterns

configcraft.inherit.DEFAULTS = '_defaults'

Special key used to define default values that can be inherited by other configuration sections.

configcraft.inherit.make_type_error(prefix: str, key: str) TypeError[source]

Create a descriptive TypeError when trying to set a value on incompatible data types.

This helper creates user-friendly error messages when the inheritance process encounters data that isn’t a dict or list of dicts, which are the only structures that support key assignment.

Parameters:
  • prefix – The JSON path prefix where the error occurred

  • key – The key we were trying to set

Raises:

TypeError – with descriptive message about the invalid operation

configcraft.inherit.inherit_value(path: str, value: Any, data: Dict[str, Any] | List[Dict[str, Any]], _prefix: str | None = None) None[source]

Apply a default value to a JSON path pattern, preserving existing values.

This is the core inheritance mechanism that implements setdefault-like behavior for nested configuration structures. Like dict.setdefault(), it only sets values where keys don’t already exist, never overwriting existing configuration.

What it does

  • Follows JSON path patterns like "*.username" or "dev.database.port"

  • Sets values only where they’re missing (non-destructive)

  • Handles wildcards (*) to apply to multiple targets

  • Works with nested dicts and lists of dicts

Examples

  • Path "*.memory" -> Sets memory=2 in all top-level environments

  • Path "dev.db.port" -> Sets port=5432 only in dev.db

  • Path "*.servers.*.cpu" -> Sets cpu=1 in all servers across all environments

Parameters:
  • path – JSON path pattern (e.g., "*.username", "dev.db.port")

  • value – The default value to set

  • data – Configuration dict/list to modify in-place

  • _prefix – Internal recursion parameter (do not use)

Raises:
  • ValueError – If path ends with “*” (incomplete path)

  • TypeError – If trying to set values on incompatible data types

Returns:

None

Important

The input param data will be modified in-place. If you want to keep the original data, do this before calling this function:

import copy

new_data = copy.deepcopy(data)
inherit_value(path, value, new_data)
configcraft.inherit.apply_inheritance(data: dict[str, Any]) None[source]

Transform configuration data by applying all _defaults inheritance rules.

This is the main entry point that processes an entire configuration structure, finding all _defaults sections and applying their inheritance rules to create the final resolved configuration.

What it does:

  1. Recursively processes nested _defaults sections (deeper ones override shallower ones)

  2. Applies each JSON path pattern in the _defaults section in definition order

  3. Removes all _defaults sections from the final output

  4. Modifies the input data in-place

Path Execution Order Within Same _defaults:

Within a single _defaults section, paths are processed from top to bottom. If multiple paths affect the same node, the earlier path takes effect due to setdefault behavior. This enables powerful exception-then-default patterns.

Example - setting defaults with specific exceptions:

{
    "_defaults": {
        "*.servers.blue.cpu": 4,    # Exception: blue gets 4 CPU
        "*.servers.*.cpu": 2        # Default: all others get 2 CPU  
    },
    "env": {
        "servers": {
            "blue": {},             # Gets cpu=4 (from first rule)
            "green": {}             # Gets cpu=2 (from second rule)
        }
    }
}

The exception must be defined before the wildcard pattern to take effect.

Child _defaults Overrides Parent _defaults:

Each nested object can have its own _defaults section. When both parent and child _defaults sections would affect the same node, the child wins due to processing order (children processed before parents).

Example - nested inheritance hierarchy:

{
    "_defaults": {
        "*.servers.*.memory": 1024  # Parent default
    },
    "env": {
        "servers": {
            "_defaults": {
                "*.memory": 2048    # Child override
            },
            "web": {}               # Gets memory=2048 (child wins)
        }
    }
}

This design allows fine-grained control where specific sections can override broader defaults while maintaining the inheritance hierarchy.

Basic Example:

>>> data = {
...     "_defaults": {
...         "*.memory": 2
...     },
...     "dev": {},
...     "prod": {
...         "memory": 8
...     }
... }
>>> apply_inheritance(data)
>>> data
{
    "dev": {"memory": 2},     # Inherited default
    "prod": {"memory": 8}     # Kept existing value
}
Parameters:

data – Configuration dictionary with _defaults sections to process

Important

The input param data will be modified in-place, all _defaults sections will be removed and their rules applied. If you want to keep the original data, do this before calling this function:

import copy

new_data = copy.deepcopy(data)
apply_inheritance(new_data)