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 _shared section to define default values that automatically inherit to other sections, while allowing environment-specific overrides.

How It Works

The _shared 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:

{
    "_shared": {
        "*.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 _shared
        "password": "dev123",       # Original value
        "memory": 2                 # Inherited from _shared
    },
    "prod": {
        "username": "root",         # Inherited from _shared
        "password": "prod456",      # Original value
        "memory": 8                 # Override (not replaced)
    }
}

JSON Path Patterns

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

  • 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 _shared sections for fine-grained control

  • Flexible: Works with dictionaries and lists of dictionaries

  • Order-aware: Evaluation order matters for overlapping patterns

configcraft.inherit.SHARED = '_shared'

Special key used to define shared inheritance rules in configuration data.

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 _shared inheritance rules.

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

What it does:

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

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

  3. Removes all _shared sections from the final output

  4. Modifies the input data in-place

Path Execution Order Within Same _shared:

Within a single _shared 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:

{
    "_shared": {
        "*.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 _shared Overrides Parent _shared:

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

Example - nested inheritance hierarchy:

{
    "_shared": {
        "*.servers.*.memory": 1024  # Parent default
    },
    "env": {
        "servers": {
            "_shared": {
                "*.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 = {
...     "_shared": {
...         "*.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 _shared sections to process

Important

The input param data will be modified in-place, all _shared 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)