{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Hierarchical Configuration Inheritance Pattern: A Complete Guide\n", "\n", "## The Problem: Configuration Duplication\n", "\n", "### Why Traditional Configuration Management Is Painful\n", "\n", "Imagine you're building a microservice that needs to run in multiple environments. Without inheritance patterns, your configuration might look like this:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.421882Z", "start_time": "2023-05-31T00:11:58.414384Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "# ❌ Traditional approach - lots of duplication\n", "traditional_config = {\n", " \"dev\": {\n", " \"database\": {\n", " \"host\": \"dev-db.example.com\",\n", " \"port\": 5432,\n", " \"pool_size\": 10,\n", " \"timeout\": 30,\n", " \"ssl_mode\": \"prefer\",\n", " \"retry_attempts\": 3\n", " },\n", " \"redis\": {\n", " \"host\": \"dev-redis.example.com\", \n", " \"port\": 6379,\n", " \"timeout\": 10,\n", " \"pool_size\": 20\n", " },\n", " \"logging\": {\n", " \"level\": \"DEBUG\",\n", " \"format\": \"detailed\"\n", " }\n", " },\n", " \"staging\": {\n", " \"database\": {\n", " \"host\": \"staging-db.example.com\",\n", " \"port\": 5432, # 🔄 DUPLICATE\n", " \"pool_size\": 10, # 🔄 DUPLICATE\n", " \"timeout\": 30, # 🔄 DUPLICATE\n", " \"ssl_mode\": \"prefer\", # 🔄 DUPLICATE\n", " \"retry_attempts\": 3 # 🔄 DUPLICATE\n", " },\n", " \"redis\": {\n", " \"host\": \"staging-redis.example.com\",\n", " \"port\": 6379, # 🔄 DUPLICATE\n", " \"timeout\": 10, # 🔄 DUPLICATE\n", " \"pool_size\": 20 # 🔄 DUPLICATE\n", " },\n", " \"logging\": {\n", " \"level\": \"INFO\",\n", " \"format\": \"detailed\" # 🔄 DUPLICATE\n", " }\n", " },\n", " \"prod\": {\n", " \"database\": {\n", " \"host\": \"prod-db.example.com\",\n", " \"port\": 5432, # 🔄 DUPLICATE\n", " \"pool_size\": 50, # Different value, but pattern repeats\n", " \"timeout\": 30, # 🔄 DUPLICATE\n", " \"ssl_mode\": \"require\", # Different value\n", " \"retry_attempts\": 3 # 🔄 DUPLICATE\n", " },\n", " \"redis\": {\n", " \"host\": \"prod-redis.example.com\",\n", " \"port\": 6379, # 🔄 DUPLICATE\n", " \"timeout\": 10, # 🔄 DUPLICATE\n", " \"pool_size\": 50 # Different value\n", " },\n", " \"logging\": {\n", " \"level\": \"ERROR\",\n", " \"format\": \"detailed\" # 🔄 DUPLICATE\n", " }\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The Pain Points\n", "\n", "1. **🔄 Massive Duplication**: 80% of configuration values are repeated across environments\n", "2. **🐛 Error Prone**: Change a default port? You must remember to update it in 3+ places\n", "3. **📈 Scales Poorly**: Adding a new environment means copying and modifying everything\n", "4. **🔍 Hard to Understand**: What values are defaults vs environment-specific overrides?\n", "5. **🚀 Maintenance Nightmare**: Updating shared settings requires touching multiple sections\n", "\n", "---\n", "\n", "## The Solution: Hierarchical Inheritance\n", "\n", "### The DRY (Do Not Repeat Yourself) Approach with `_defaults`\n", "\n", "The hierarchical pattern solves this by introducing a **`_defaults` section** that defines defaults, which automatically inherit to all environments unless specifically overridden:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.457569Z", "start_time": "2023-05-31T00:11:58.419705Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "# ✅ Hierarchical approach - DRY and maintainable\n", "hierarchical_config = {\n", " \"_defaults\": {\n", " # 🎯 Define defaults ONCE\n", " \"*.database.port\": 5432,\n", " \"*.database.pool_size\": 10,\n", " \"*.database.timeout\": 30,\n", " \"*.database.ssl_mode\": \"prefer\",\n", " \"*.database.retry_attempts\": 3,\n", " \"*.redis.port\": 6379,\n", " \"*.redis.timeout\": 10,\n", " \"*.redis.pool_size\": 20,\n", " \"*.logging.format\": \"detailed\"\n", " },\n", " \"dev\": {\n", " \"database\": {\n", " \"host\": \"dev-db.example.com\"\n", " # 👆 All other database settings inherited from _defaults\n", " },\n", " \"redis\": {\n", " \"host\": \"dev-redis.example.com\"\n", " # 👆 All other redis settings inherited from _defaults\n", " },\n", " \"logging\": {\n", " \"level\": \"DEBUG\"\n", " # 👆 format inherited from _defaults\n", " }\n", " },\n", " \"staging\": {\n", " \"database\": {\"host\": \"staging-db.example.com\"},\n", " \"redis\": {\"host\": \"staging-redis.example.com\"},\n", " \"logging\": {\"level\": \"INFO\"}\n", " },\n", " \"prod\": {\n", " \"database\": {\n", " \"host\": \"prod-db.example.com\",\n", " \"pool_size\": 50, # 🎯 Override default for production\n", " \"ssl_mode\": \"require\" # 🎯 Override default for production\n", " },\n", " \"redis\": {\n", " \"host\": \"prod-redis.example.com\",\n", " \"pool_size\": 50 # 🎯 Override default for production\n", " },\n", " \"logging\": {\"level\": \"ERROR\"}\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Core Concepts\n", "\n", "### Setup" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.463596Z", "start_time": "2023-05-31T00:11:58.459827Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "import json\n", "from rich import print as rprint\n", "from configcraft.api import DEFAULTS, apply_inheritance, inherit_value\n", "\n", "def jprint(data: dict):\n", " \"\"\"Pretty print JSON data\"\"\"\n", " rprint(json.dumps(data, indent=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. The `_defaults` Section\n", "\n", "The `_defaults` section is a **meta-configuration** that defines inheritable defaults:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.467856Z", "start_time": "2023-05-31T00:11:58.465027Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "config = {\n", " \"_defaults\": {\n", " \"*.timeout\": 30, # Apply to all environments\n", " \"*.retry_attempts\": 3 # Apply to all environments \n", " },\n", " \"dev\": {\"host\": \"dev.com\"},\n", " \"prod\": {\"host\": \"prod.com\"}\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. JSON Path Patterns\n", "\n", "JSON paths specify **where** default values should be applied:\n", "\n", "| Pattern | Meaning | Example |\n", "|---------|---------|---------|\n", "| `*.field` | All top-level keys | `*.timeout` → applies to dev.timeout, prod.timeout |\n", "| `env.field` | Specific environment | `dev.timeout` → applies only to dev.timeout |\n", "| `*.service.field` | Nested paths | `*.db.port` → applies to dev.db.port, prod.db.port |\n", "| `*.services.*.field` | Multiple wildcards | `*.apps.*.memory` → all apps in all environments |\n", "\n", "### 3. Non-Destructive Inheritance\n", "\n", "**Key Principle**: Default values are only applied when the target key **doesn't already exist**." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.475140Z", "start_time": "2023-05-31T00:11:58.468910Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "config = {\n", " \"_defaults\": {\"*.memory\": 2},\n", " \"dev\": {}, # ✅ Will get memory: 2\n", " \"prod\": {\"memory\": 8} # ✅ Keeps existing memory: 8\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Basic Usage\n", "\n", "### Example 1: Simple Environment Defaults" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.479906Z", "start_time": "2023-05-31T00:11:58.475872Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"memory\": 2,\n", " \"cpu\": 1\n", " },\n", " \"staging\": {\n", " \"memory\": 4,\n", " \"cpu\": 1\n", " },\n", " \"prod\": {\n", " \"memory\": 8,\n", " \"cpu\": 4\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m2\u001b[0m,\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m1\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"staging\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m4\u001b[0m,\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m1\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m8\u001b[0m,\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m4\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Define configuration with default values\n", "config_data = {\n", " \"_defaults\": {\n", " \"*.memory\": 2, # Default memory for all environments\n", " \"*.cpu\": 1 # Default CPU for all environments\n", " },\n", " \"dev\": {}, # Empty - will inherit all defaults\n", " \"staging\": {\n", " \"memory\": 4 # Override memory, inherit CPU\n", " },\n", " \"prod\": {\n", " \"memory\": 8, # Override memory\n", " \"cpu\": 4 # Override CPU\n", " }\n", "}\n", "\n", "# Apply inheritance\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 2: Nested Configuration Inheritance" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2023-05-31T00:11:58.527338Z", "start_time": "2023-05-31T00:11:58.480775Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"database\": {\n", " \"host\": \"localhost\",\n", " \"port\": 5432,\n", " \"pool_size\": 10\n", " },\n", " \"cache\": {\n", " \"host\": \"localhost\",\n", " \"ttl\": 3600\n", " }\n", " },\n", " \"prod\": {\n", " \"database\": {\n", " \"host\": \"prod-db.com\",\n", " \"pool_size\": 50,\n", " \"port\": 5432\n", " },\n", " \"cache\": {\n", " \"host\": \"prod-cache.com\",\n", " \"ttl\": 7200\n", " }\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"database\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"localhost\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m10\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"cache\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"localhost\"\u001b[0m,\n", " \u001b[32m\"ttl\"\u001b[0m: \u001b[1;36m3600\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"database\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod-db.com\"\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m50\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"cache\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod-cache.com\"\u001b[0m,\n", " \u001b[32m\"ttl\"\u001b[0m: \u001b[1;36m7200\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "config_data = {\n", " \"_defaults\": {\n", " \"*.database.port\": 5432,\n", " \"*.database.pool_size\": 10,\n", " \"*.cache.ttl\": 3600\n", " },\n", " \"dev\": {\n", " \"database\": {\n", " \"host\": \"localhost\"\n", " # port and pool_size will be inherited\n", " },\n", " \"cache\": {\n", " \"host\": \"localhost\"\n", " # ttl will be inherited\n", " }\n", " },\n", " \"prod\": {\n", " \"database\": {\n", " \"host\": \"prod-db.com\",\n", " \"pool_size\": 50 # Override default\n", " # port will be inherited\n", " },\n", " \"cache\": {\n", " \"host\": \"prod-cache.com\",\n", " \"ttl\": 7200 # Override default\n", " }\n", " }\n", "}\n", "\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Path Execution Order: Exception-Then-Default Pattern\n", "\n", "🚨 **Critical Behavior**: Within a single `_defaults` section, paths are processed **from top to bottom**. If multiple paths affect the same node, the **earlier path wins** due to `setdefault` behavior.\n", "\n", "This enables powerful **exception-then-default** patterns:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"servers\": {\n", " \"web\": {\n", " \"cpu\": 2\n", " },\n", " \"high_memory\": {\n", " \"cpu\": 8\n", " },\n", " \"worker\": {\n", " \"cpu\": 2\n", " }\n", " }\n", " },\n", " \"prod\": {\n", " \"servers\": {\n", " \"web\": {\n", " \"cpu\": 2\n", " },\n", " \"high_memory\": {\n", " \"cpu\": 8\n", " },\n", " \"database\": {\n", " \"cpu\": 16\n", " }\n", " }\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"servers\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m2\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"high_memory\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m8\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m2\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"servers\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m2\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"high_memory\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m8\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"database\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"cpu\"\u001b[0m: \u001b[1;36m16\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Example: CPU allocation with exceptions\n", "config_data = {\n", " \"_defaults\": {\n", " # ⚠️ ORDER MATTERS! Exception MUST come first\n", " \"*.servers.high_memory.cpu\": 8, # Exception: high_memory gets 8 CPU\n", " \"*.servers.*.cpu\": 2 # Default: all other servers get 2 CPU\n", " },\n", " \"dev\": {\n", " \"servers\": {\n", " \"web\": {}, # Gets cpu=2 (default rule)\n", " \"high_memory\": {}, # Gets cpu=8 (exception rule)\n", " \"worker\": {} # Gets cpu=2 (default rule)\n", " }\n", " },\n", " \"prod\": {\n", " \"servers\": {\n", " \"web\": {}, # Gets cpu=2 (default rule) \n", " \"high_memory\": {}, # Gets cpu=8 (exception rule)\n", " \"database\": {\"cpu\": 16} # Keeps cpu=16 (existing value)\n", " }\n", " }\n", "}\n", "\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**❌ Wrong Order Example:**" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# This WON'T work as expected - wrong order!\n", "config_data = {\n", " \"_defaults\": {\n", " \"*.servers.*.cpu\": 2, # 🚫 Default comes first\n", " \"*.servers.high_memory.cpu\": 8 # 🚫 Exception comes second - TOO LATE!\n", " },\n", " \"dev\": {\n", " \"servers\": {\n", " \"high_memory\": {} # Gets cpu=2 (not 8!) because default ran first\n", " }\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**✅ Design Logic:**\n", "\n", "The child-override-parent behavior follows the inheritance processing order:\n", "1. **Recursive Processing**: Children are processed before parents\n", "2. **Child `_defaults`** runs first → sets `dev.services.web.memory = 2048`\n", "3. **Parent `_defaults`** runs later → tries to set `dev.services.web.memory = 1024`, but key exists → **ignored**\n", "4. **Result**: Child settings take precedence, parent fills gaps\n", "\n", "**🎯 Real-World Use Cases:**\n", "\n", "1. **Exception Handling**: Set specific values before wildcards\n", "2. **Environment Overrides**: Child environments override global defaults \n", "3. **Service Specialization**: Specific services override category defaults\n", "4. **Progressive Refinement**: Broad defaults → environment defaults → service specifics\n", "\n", "### Working with Lists of Objects\n", "\n", "The inheritance pattern works seamlessly with **lists of dictionaries**:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"services\": {\n", " \"web\": {\n", " \"memory\": 2048,\n", " \"log_level\": \"DEBUG\",\n", " \"timeout\": 30\n", " },\n", " \"worker\": {\n", " \"memory\": 4096,\n", " \"log_level\": \"DEBUG\",\n", " \"timeout\": 30\n", " }\n", " }\n", " },\n", " \"prod\": {\n", " \"services\": {\n", " \"web\": {\n", " \"memory\": 1024,\n", " \"timeout\": 30\n", " },\n", " \"worker\": {\n", " \"memory\": 1024,\n", " \"timeout\": 30\n", " }\n", " }\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"services\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m2048\u001b[0m,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"DEBUG\"\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m4096\u001b[0m,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"DEBUG\"\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"services\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m1024\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m1024\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Example: Nested inheritance hierarchy\n", "config_data = {\n", " \"_defaults\": {\n", " \"*.services.*.memory\": 1024, # Parent default: 1GB for all services\n", " \"*.services.*.timeout\": 30 # Parent default: 30s timeout\n", " },\n", " \"dev\": {\n", " \"services\": {\n", " \"_defaults\": {\n", " \"*.memory\": 2048, # Child override: dev services get 2GB \n", " \"*.log_level\": \"DEBUG\" # Child addition: dev-specific setting\n", " },\n", " \"web\": {}, # Gets memory=2048 (child), timeout=30 (parent), log_level=DEBUG (child)\n", " \"worker\": {\"memory\": 4096} # Gets memory=4096 (explicit), timeout=30 (parent), log_level=DEBUG (child)\n", " }\n", " },\n", " \"prod\": {\n", " \"services\": {\n", " \"web\": {}, # Gets memory=1024 (parent), timeout=30 (parent)\n", " \"worker\": {} # Gets memory=1024 (parent), timeout=30 (parent)\n", " }\n", " }\n", "}\n", "\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**✅ Design Logic:**\n", "\n", "The child-override-parent behavior follows the inheritance processing order:\n", "1. **Recursive Processing**: Children are processed before parents\n", "2. **Child `_defaults`** runs first → sets `dev.services.web.memory = 2048`\n", "3. **Parent `_defaults`** runs later → tries to set `dev.services.web.memory = 1024`, but key exists → **ignored**\n", "4. **Result**: Child settings take precedence, parent fills gaps\n", "\n", "**🎯 Real-World Use Cases:**\n", "\n", "1. **Exception Handling**: Set specific values before wildcards\n", "2. **Environment Overrides**: Child environments override global defaults \n", "3. **Service Specialization**: Specific services override category defaults\n", "4. **Progressive Refinement**: Broad defaults → environment defaults → service specifics\n", "\n", "### Working with Lists of Objects\n", "\n", "The inheritance pattern works seamlessly with **lists of dictionaries**:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"databases\": [\n", " {\n", " \"host\": \"dev-primary.com\",\n", " \"type\": \"primary\",\n", " \"port\": 5432,\n", " \"timeout\": 30\n", " },\n", " {\n", " \"host\": \"dev-replica.com\",\n", " \"type\": \"replica\",\n", " \"port\": 5432,\n", " \"timeout\": 30\n", " }\n", " ]\n", " },\n", " \"prod\": {\n", " \"databases\": [\n", " {\n", " \"host\": \"prod-primary.com\",\n", " \"type\": \"primary\",\n", " \"port\": 5432,\n", " \"timeout\": 30\n", " },\n", " {\n", " \"host\": \"prod-replica.com\",\n", " \"type\": \"replica\",\n", " \"port\": 5433,\n", " \"timeout\": 30\n", " }\n", " ]\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"databases\"\u001b[0m: \u001b[1m[\u001b[0m\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"dev-primary.com\"\u001b[0m,\n", " \u001b[32m\"type\"\u001b[0m: \u001b[32m\"primary\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"dev-replica.com\"\u001b[0m,\n", " \u001b[32m\"type\"\u001b[0m: \u001b[32m\"replica\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"databases\"\u001b[0m: \u001b[1m[\u001b[0m\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod-primary.com\"\u001b[0m,\n", " \u001b[32m\"type\"\u001b[0m: \u001b[32m\"primary\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod-replica.com\"\u001b[0m,\n", " \u001b[32m\"type\"\u001b[0m: \u001b[32m\"replica\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5433\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "config_data = {\n", " \"_defaults\": {\n", " \"*.databases.port\": 5432, # Apply to ALL database objects\n", " \"*.databases.timeout\": 30 # Apply to ALL database objects\n", " },\n", " \"dev\": {\n", " \"databases\": [\n", " {\"host\": \"dev-primary.com\", \"type\": \"primary\"},\n", " {\"host\": \"dev-replica.com\", \"type\": \"replica\"}\n", " # Both will inherit port and timeout\n", " ]\n", " },\n", " \"prod\": {\n", " \"databases\": [\n", " {\"host\": \"prod-primary.com\", \"type\": \"primary\"},\n", " {\"host\": \"prod-replica.com\", \"type\": \"replica\", \"port\": 5433}\n", " # First inherits port, second keeps override\n", " ]\n", " }\n", "}\n", "\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Specific Environment Targeting\n", "\n", "Sometimes you want to set defaults for **specific environments only**:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"web\": {\n", " \"memory\": 4\n", " },\n", " \"worker\": {\n", " \"memory\": 4\n", " },\n", " \"log_level\": \"INFO\"\n", " },\n", " \"staging\": {\n", " \"web\": {},\n", " \"worker\": {\n", " \"memory\": 8\n", " },\n", " \"log_level\": \"INFO\"\n", " },\n", " \"prod\": {\n", " \"web\": {\n", " \"memory\": 16\n", " },\n", " \"worker\": {\n", " \"memory\": 32\n", " },\n", " \"log_level\": \"INFO\"\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m4\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m4\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"INFO\"\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"staging\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m8\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"INFO\"\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m16\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m32\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"INFO\"\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "config_data = {\n", " \"_defaults\": {\n", " \"dev.*.memory\": 4, # Only dev environments get 4GB\n", " \"prod.*.memory\": 16, # Only prod environments get 16GB\n", " \"*.log_level\": \"INFO\" # All environments get INFO logging\n", " },\n", " \"dev\": {\n", " \"web\": {}, # Will get memory: 4, log_level: \"INFO\"\n", " \"worker\": {} # Will get memory: 4, log_level: \"INFO\"\n", " },\n", " \"staging\": {\n", " \"web\": {}, # Will get log_level: \"INFO\" only\n", " \"worker\": {\"memory\": 8} # Custom memory, inherits log_level\n", " },\n", " \"prod\": {\n", " \"web\": {}, # Will get memory: 16, log_level: \"INFO\"\n", " \"worker\": {\"memory\": 32} # Custom memory, inherits log_level\n", " }\n", "}\n", "\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Nested `_defaults` Sections (Advanced Override)\n", "\n", "You can have **multiple levels** of `_defaults` sections for fine-grained control:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"web\": {\n", " \"memory\": 4,\n", " \"debug\": true,\n", " \"log_level\": \"INFO\"\n", " },\n", " \"worker\": {\n", " \"memory\": 8,\n", " \"debug\": true,\n", " \"log_level\": \"INFO\"\n", " }\n", " },\n", " \"prod\": {\n", " \"web\": {\n", " \"log_level\": \"ERROR\",\n", " \"memory\": 2\n", " },\n", " \"worker\": {\n", " \"log_level\": \"ERROR\",\n", " \"memory\": 2\n", " }\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m4\u001b[0m,\n", " \u001b[32m\"debug\"\u001b[0m: true,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"INFO\"\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m8\u001b[0m,\n", " \u001b[32m\"debug\"\u001b[0m: true,\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"INFO\"\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"web\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"ERROR\"\u001b[0m,\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m2\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"worker\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"log_level\"\u001b[0m: \u001b[32m\"ERROR\"\u001b[0m,\n", " \u001b[32m\"memory\"\u001b[0m: \u001b[1;36m2\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "config_data = {\n", " \"_defaults\": {\n", " \"*.*.memory\": 2, # Global default: 2GB for all services\n", " \"*.*.log_level\": \"INFO\" # Global default: INFO logging\n", " },\n", " \"dev\": {\n", " \"_defaults\": {\n", " \"*.memory\": 4, # Dev-specific: Override memory to 4GB\n", " \"*.debug\": True # Dev-specific: Enable debug mode\n", " },\n", " \"web\": {}, # Gets: memory=4, log_level=\"INFO\", debug=True\n", " \"worker\": {\"memory\": 8} # Gets: memory=8 (override), log_level=\"INFO\", debug=True\n", " },\n", " \"prod\": {\n", " \"_defaults\": {\n", " \"*.log_level\": \"ERROR\" # Prod-specific: Only log errors\n", " },\n", " \"web\": {}, # Gets: memory=2, log_level=\"ERROR\"\n", " \"worker\": {} # Gets: memory=2, log_level=\"ERROR\"\n", " }\n", "}\n", "\n", "apply_inheritance(config_data)\n", "jprint(config_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Understanding the API\n", "\n", "The hierarchical configuration pattern provides two main functions with different purposes:\n", "\n", "### `inherit_value()` - Low-Level Inheritance\n", "\n", "This is the **core building block** that applies a single shared value to its target location(s):" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"host\": \"dev.com\",\n", " \"port\": 3000\n", " },\n", " \"prod\": {\n", " \"host\": \"prod.com\",\n", " \"port\": 8080\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"dev.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m3000\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m8080\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from configcraft.api import inherit_value\n", "\n", "# Example: Set a default value only where it doesn't exist\n", "data = {\n", " \"dev\": {\"host\": \"dev.com\"},\n", " \"prod\": {\"host\": \"prod.com\", \"port\": 8080} # Already has port\n", "}\n", "\n", "# Apply default port to all environments\n", "inherit_value(path=\"*.port\", value=3000, data=data)\n", "\n", "jprint(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `apply_inheritance()` - High-Level Configuration Processing\n", "\n", "This is the **main entry point** that processes entire configuration structures with `_defaults` sections:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"host\": \"dev.com\",\n", " \"port\": 3000,\n", " \"timeout\": 30\n", " },\n", " \"prod\": {\n", " \"host\": \"prod.com\",\n", " \"port\": 8080,\n", " \"timeout\": 30\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"dev.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m3000\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m8080\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Example: Process a complete configuration\n", "config = {\n", " \"_defaults\": {\n", " \"*.port\": 3000,\n", " \"*.timeout\": 30\n", " },\n", " \"dev\": {\"host\": \"dev.com\"},\n", " \"prod\": {\"host\": \"prod.com\", \"port\": 8080}\n", "}\n", "\n", "apply_inheritance(config)\n", "\n", "jprint(config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**When to use `apply_inheritance()`:**\n", "- ✅ Processing complete configuration files\n", "- ✅ Standard use case with `_defaults` sections\n", "- ✅ Production configuration management\n", "- ✅ Most common use case - start here!\n", "\n", "---\n", "\n", "## Real-World Examples\n", "\n", "### Example 1: Microservice Configuration" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Real-world microservice configuration\n", "microservice_config = {\n", " \"_defaults\": {\n", " # Database defaults\n", " \"*.database.pool_size\": 10,\n", " \"*.database.timeout\": 30,\n", " \"*.database.retry_attempts\": 3,\n", " \n", " # Redis defaults \n", " \"*.redis.timeout\": 5,\n", " \"*.redis.pool_size\": 20,\n", " \n", " # Logging defaults\n", " \"*.logging.format\": \"json\",\n", " \"*.logging.level\": \"INFO\",\n", " \n", " # HTTP defaults\n", " \"*.http.timeout\": 10,\n", " \"*.http.retry_attempts\": 3\n", " },\n", " \"local\": {\n", " \"database\": {\n", " \"host\": \"localhost\",\n", " \"port\": 5432,\n", " \"name\": \"myapp_dev\"\n", " },\n", " \"redis\": {\n", " \"host\": \"localhost\", \n", " \"port\": 6379\n", " },\n", " \"logging\": {\n", " \"level\": \"DEBUG\" # Override for local development\n", " },\n", " \"http\": {\n", " \"base_url\": \"http://localhost:8000\"\n", " }\n", " },\n", " \"staging\": {\n", " \"database\": {\n", " \"host\": \"staging-db.company.com\",\n", " \"port\": 5432,\n", " \"name\": \"myapp_staging\",\n", " \"pool_size\": 20 # Override for staging load\n", " },\n", " \"redis\": {\n", " \"host\": \"staging-redis.company.com\",\n", " \"port\": 6379\n", " },\n", " \"logging\": {},\n", " \"http\": {\n", " \"base_url\": \"https://staging-api.company.com\"\n", " }\n", " },\n", " \"production\": {\n", " \"database\": {\n", " \"host\": \"prod-db.company.com\",\n", " \"port\": 5432,\n", " \"name\": \"myapp_prod\",\n", " \"pool_size\": 50, # Production needs more connections\n", " \"timeout\": 60 # Production can wait longer\n", " },\n", " \"redis\": {\n", " \"host\": \"prod-redis.company.com\",\n", " \"port\": 6379,\n", " \"pool_size\": 100 # Production needs larger pool\n", " },\n", " \"logging\": {\n", " \"level\": \"ERROR\" # Production only logs errors\n", " },\n", " \"http\": {\n", " \"base_url\": \"https://api.company.com\",\n", " \"timeout\": 30 # Production can wait longer\n", " }\n", " }\n", "}\n", "\n", "apply_inheritance(microservice_config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After processing, each environment gets:\n", "- ✅ All the appropriate defaults from `_defaults`\n", "- ✅ Environment-specific host/URL configurations \n", "- ✅ Performance tuning overrides where needed\n", "- ✅ No duplication of common settings" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"local\": {\n", " \"database\": {\n", " \"host\": \"localhost\",\n", " \"port\": 5432,\n", " \"name\": \"myapp_dev\",\n", " \"pool_size\": 10,\n", " \"timeout\": 30,\n", " \"retry_attempts\": 3\n", " },\n", " \"redis\": {\n", " \"host\": \"localhost\",\n", " \"port\": 6379,\n", " \"timeout\": 5,\n", " \"pool_size\": 20\n", " },\n", " \"logging\": {\n", " \"level\": \"DEBUG\",\n", " \"format\": \"json\"\n", " },\n", " \"http\": {\n", " \"base_url\": \"http://localhost:8000\",\n", " \"timeout\": 10,\n", " \"retry_attempts\": 3\n", " }\n", " },\n", " \"staging\": {\n", " \"database\": {\n", " \"host\": \"staging-db.company.com\",\n", " \"port\": 5432,\n", " \"name\": \"myapp_staging\",\n", " \"pool_size\": 20,\n", " \"timeout\": 30,\n", " \"retry_attempts\": 3\n", " },\n", " \"redis\": {\n", " \"host\": \"staging-redis.company.com\",\n", " \"port\": 6379,\n", " \"timeout\": 5,\n", " \"pool_size\": 20\n", " },\n", " \"logging\": {\n", " \"format\": \"json\",\n", " \"level\": \"INFO\"\n", " },\n", " \"http\": {\n", " \"base_url\": \"https://staging-api.company.com\",\n", " \"timeout\": 10,\n", " \"retry_attempts\": 3\n", " }\n", " },\n", " \"production\": {\n", " \"database\": {\n", " \"host\": \"prod-db.company.com\",\n", " \"port\": 5432,\n", " \"name\": \"myapp_prod\",\n", " \"pool_size\": 50,\n", " \"timeout\": 60,\n", " \"retry_attempts\": 3\n", " },\n", " \"redis\": {\n", " \"host\": \"prod-redis.company.com\",\n", " \"port\": 6379,\n", " \"pool_size\": 100,\n", " \"timeout\": 5\n", " },\n", " \"logging\": {\n", " \"level\": \"ERROR\",\n", " \"format\": \"json\"\n", " },\n", " \"http\": {\n", " \"base_url\": \"https://api.company.com\",\n", " \"timeout\": 30,\n", " \"retry_attempts\": 3\n", " }\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"local\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"database\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"localhost\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"name\"\u001b[0m: \u001b[32m\"myapp_dev\"\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m10\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m,\n", " \u001b[32m\"retry_attempts\"\u001b[0m: \u001b[1;36m3\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"redis\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"localhost\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m6379\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m5\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m20\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"logging\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"level\"\u001b[0m: \u001b[32m\"DEBUG\"\u001b[0m,\n", " \u001b[32m\"format\"\u001b[0m: \u001b[32m\"json\"\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"http\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"base_url\"\u001b[0m: \u001b[32m\"http://localhost:8000\"\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m10\u001b[0m,\n", " \u001b[32m\"retry_attempts\"\u001b[0m: \u001b[1;36m3\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"staging\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"database\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"staging-db.company.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"name\"\u001b[0m: \u001b[32m\"myapp_staging\"\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m20\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m,\n", " \u001b[32m\"retry_attempts\"\u001b[0m: \u001b[1;36m3\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"redis\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"staging-redis.company.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m6379\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m5\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m20\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"logging\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"format\"\u001b[0m: \u001b[32m\"json\"\u001b[0m,\n", " \u001b[32m\"level\"\u001b[0m: \u001b[32m\"INFO\"\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"http\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"base_url\"\u001b[0m: \u001b[32m\"https://staging-api.company.com\"\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m10\u001b[0m,\n", " \u001b[32m\"retry_attempts\"\u001b[0m: \u001b[1;36m3\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"production\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"database\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod-db.company.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m5432\u001b[0m,\n", " \u001b[32m\"name\"\u001b[0m: \u001b[32m\"myapp_prod\"\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m50\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m60\u001b[0m,\n", " \u001b[32m\"retry_attempts\"\u001b[0m: \u001b[1;36m3\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"redis\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"host\"\u001b[0m: \u001b[32m\"prod-redis.company.com\"\u001b[0m,\n", " \u001b[32m\"port\"\u001b[0m: \u001b[1;36m6379\u001b[0m,\n", " \u001b[32m\"pool_size\"\u001b[0m: \u001b[1;36m100\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m5\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"logging\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"level\"\u001b[0m: \u001b[32m\"ERROR\"\u001b[0m,\n", " \u001b[32m\"format\"\u001b[0m: \u001b[32m\"json\"\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"http\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"base_url\"\u001b[0m: \u001b[32m\"https://api.company.com\"\u001b[0m,\n", " \u001b[32m\"timeout\"\u001b[0m: \u001b[1;36m30\u001b[0m,\n", " \u001b[32m\"retry_attempts\"\u001b[0m: \u001b[1;36m3\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "jprint(microservice_config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 2: Multi-Tenant SaaS Configuration" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "# SaaS application with multiple tenants\n", "saas_config = {\n", " \"_defaults\": {\n", " # Default resource limits\n", " \"*.tenants.*.cpu_limit\": 1,\n", " \"*.tenants.*.memory_limit\": 2, \n", " \"*.tenants.*.storage_limit\": 10,\n", " \n", " # Default feature flags\n", " \"*.tenants.*.features.analytics\": True,\n", " \"*.tenants.*.features.api_access\": True,\n", " \"*.tenants.*.features.custom_domain\": False,\n", " \n", " # Default billing\n", " \"*.tenants.*.billing.plan\": \"basic\",\n", " \"*.tenants.*.billing.trial_days\": 14\n", " },\n", " \"dev\": {\n", " \"tenants\": {\n", " \"test_tenant\": {\n", " \"name\": \"Test Company\",\n", " # Gets all defaults\n", " \"features\": {},\n", " \"billing\": {}\n", " }\n", " }\n", " },\n", " \"prod\": {\n", " \"tenants\": {\n", " \"startup_co\": {\n", " \"name\": \"Startup Co\",\n", " \"billing\": {\"plan\": \"startup\"}, # Override plan\n", " # Other defaults inherited\n", " \"features\": {},\n", " \"billing\": {}\n", " },\n", " \"enterprise_corp\": {\n", " \"name\": \"Enterprise Corp\", \n", " \"cpu_limit\": 8, # Enterprise gets more resources\n", " \"memory_limit\": 16,\n", " \"storage_limit\": 1000,\n", " \"features\": {\n", " \"custom_domain\": True, # Enterprise feature\n", " \"sso\": True # Additional enterprise feature\n", " },\n", " \"billing\": {\n", " \"plan\": \"enterprise\",\n", " \"trial_days\": 30 # Longer trial\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", "apply_inheritance(saas_config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This pattern allows you to:\n", "- 🎯 Set sensible defaults for all tenants\n", "- 🚀 Quickly onboard new tenants with minimal configuration\n", "- 💰 Easily implement tiered pricing with resource overrides\n", "- 🔧 Maintain consistent feature flags across environments" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n", " \"dev\": {\n", " \"tenants\": {\n", " \"test_tenant\": {\n", " \"name\": \"Test Company\",\n", " \"features\": {\n", " \"analytics\": true,\n", " \"api_access\": true,\n", " \"custom_domain\": false\n", " },\n", " \"billing\": {\n", " \"plan\": \"basic\",\n", " \"trial_days\": 14\n", " },\n", " \"cpu_limit\": 1,\n", " \"memory_limit\": 2,\n", " \"storage_limit\": 10\n", " }\n", " }\n", " },\n", " \"prod\": {\n", " \"tenants\": {\n", " \"startup_co\": {\n", " \"name\": \"Startup Co\",\n", " \"billing\": {\n", " \"plan\": \"basic\",\n", " \"trial_days\": 14\n", " },\n", " \"features\": {\n", " \"analytics\": true,\n", " \"api_access\": true,\n", " \"custom_domain\": false\n", " },\n", " \"cpu_limit\": 1,\n", " \"memory_limit\": 2,\n", " \"storage_limit\": 10\n", " },\n", " \"enterprise_corp\": {\n", " \"name\": \"Enterprise Corp\",\n", " \"cpu_limit\": 8,\n", " \"memory_limit\": 16,\n", " \"storage_limit\": 1000,\n", " \"features\": {\n", " \"custom_domain\": true,\n", " \"sso\": true,\n", " \"analytics\": true,\n", " \"api_access\": true\n", " },\n", " \"billing\": {\n", " \"plan\": \"enterprise\",\n", " \"trial_days\": 30\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m\"dev\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"tenants\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"test_tenant\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"name\"\u001b[0m: \u001b[32m\"Test Company\"\u001b[0m,\n", " \u001b[32m\"features\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"analytics\"\u001b[0m: true,\n", " \u001b[32m\"api_access\"\u001b[0m: true,\n", " \u001b[32m\"custom_domain\"\u001b[0m: false\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"billing\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"plan\"\u001b[0m: \u001b[32m\"basic\"\u001b[0m,\n", " \u001b[32m\"trial_days\"\u001b[0m: \u001b[1;36m14\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"cpu_limit\"\u001b[0m: \u001b[1;36m1\u001b[0m,\n", " \u001b[32m\"memory_limit\"\u001b[0m: \u001b[1;36m2\u001b[0m,\n", " \u001b[32m\"storage_limit\"\u001b[0m: \u001b[1;36m10\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"prod\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"tenants\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"startup_co\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"name\"\u001b[0m: \u001b[32m\"Startup Co\"\u001b[0m,\n", " \u001b[32m\"billing\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"plan\"\u001b[0m: \u001b[32m\"basic\"\u001b[0m,\n", " \u001b[32m\"trial_days\"\u001b[0m: \u001b[1;36m14\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"features\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"analytics\"\u001b[0m: true,\n", " \u001b[32m\"api_access\"\u001b[0m: true,\n", " \u001b[32m\"custom_domain\"\u001b[0m: false\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"cpu_limit\"\u001b[0m: \u001b[1;36m1\u001b[0m,\n", " \u001b[32m\"memory_limit\"\u001b[0m: \u001b[1;36m2\u001b[0m,\n", " \u001b[32m\"storage_limit\"\u001b[0m: \u001b[1;36m10\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"enterprise_corp\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"name\"\u001b[0m: \u001b[32m\"Enterprise Corp\"\u001b[0m,\n", " \u001b[32m\"cpu_limit\"\u001b[0m: \u001b[1;36m8\u001b[0m,\n", " \u001b[32m\"memory_limit\"\u001b[0m: \u001b[1;36m16\u001b[0m,\n", " \u001b[32m\"storage_limit\"\u001b[0m: \u001b[1;36m1000\u001b[0m,\n", " \u001b[32m\"features\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"custom_domain\"\u001b[0m: true,\n", " \u001b[32m\"sso\"\u001b[0m: true,\n", " \u001b[32m\"analytics\"\u001b[0m: true,\n", " \u001b[32m\"api_access\"\u001b[0m: true\n", " \u001b[1m}\u001b[0m,\n", " \u001b[32m\"billing\"\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m\"plan\"\u001b[0m: \u001b[32m\"enterprise\"\u001b[0m,\n", " \u001b[32m\"trial_days\"\u001b[0m: \u001b[1;36m30\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "jprint(saas_config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Best Practices\n", "\n", "### 1. Design Patterns\n", "\n", "#### ✅ DO: Start with Broad Defaults, Then Specialize" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "# Good: Broad defaults with specific overrides\n", "config = {\n", " \"_defaults\": {\n", " \"*.memory\": 2, # Broad default\n", " \"prod.*.memory\": 8 # Environment-specific override\n", " },\n", " \"dev\": {\"api\": {}, \"worker\": {}},\n", " \"prod\": {\"api\": {}, \"worker\": {\"memory\": 16}} # Service-specific override\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### ❌ DON'T: Over-specify in Defaults" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "# Bad: Too specific in _defaults\n", "config = {\n", " \"_defaults\": {\n", " \"dev.api.memory\": 2,\n", " \"dev.worker.memory\": 2,\n", " \"prod.api.memory\": 8,\n", " \"prod.worker.memory\": 8 # This defeats the purpose!\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### ✅ DO: Use Nested `_defaults` for Logical Grouping" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "# Good: Logical grouping with nested _defaults\n", "config = {\n", " \"_defaults\": {\n", " \"*.log_level\": \"INFO\" # Global setting\n", " },\n", " \"dev\": {\n", " \"_defaults\": {\n", " \"*.debug\": True, # Dev-specific settings\n", " \"*.hot_reload\": True\n", " },\n", " \"api\": {},\n", " \"worker\": {}\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Path Pattern Guidelines\n", "\n", "#### Use Wildcards Strategically" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "''" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ✅ Good patterns\n", "\"*.timeout\" # All environments\n", "\"*.database.port\" # All database configs\n", "\"prod.*.memory\" # All prod services\n", "\"*.services.*.cpu\" # All services in all environments\n", "\n", "# ❌ Avoid these patterns \n", "\"*.*.*.*\" # Too generic\n", "\"very.specific.deep.path.field\" # Too specific\n", ";" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Establish Naming Conventions" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "# ✅ Consistent naming helps pattern matching\n", "config = {\n", " \"_defaults\": {\n", " \"*.database_primary.port\": 5432,\n", " \"*.database_replica.port\": 5433,\n", " \"*.cache_redis.port\": 6379\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Configuration Organization\n", "\n", "#### Group Related Settings" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "# ✅ Well-organized configuration\n", "config = {\n", " \"_defaults\": {\n", " # Database cluster\n", " \"*.database.port\": 5432,\n", " \"*.database.pool_size\": 10,\n", " \"*.database.timeout\": 30,\n", " \n", " # Caching layer\n", " \"*.cache.ttl\": 3600,\n", " \"*.cache.max_size\": 1000,\n", " \n", " # Monitoring\n", " \"*.monitoring.enabled\": True,\n", " \"*.monitoring.interval\": 60\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Document Your Patterns" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "config = {\n", " \"_defaults\": {\n", " # Resource defaults - production overrides these\n", " \"*.memory\": 2, # GB\n", " \"*.cpu\": 1, # cores\n", " \n", " # Network timeouts - keep aggressive for responsiveness\n", " \"*.timeout\": 30, # seconds\n", " \"*.retry_attempts\": 3, # count\n", " \n", " # Feature flags - enable by default, disable selectively \n", " \"*.features.metrics\": True,\n", " \"*.features.tracing\": True\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. Test Your Configuration Processing" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "def test_config_inheritance():\n", " config = {\n", " \"_defaults\": {\"*.port\": 3000},\n", " \"dev\": {\"host\": \"localhost\"},\n", " \"prod\": {\"host\": \"prod.com\", \"port\": 8080}\n", " }\n", " \n", " apply_inheritance(config)\n", " \n", " # Validate inheritance worked\n", " assert config[\"dev\"][\"port\"] == 3000 # Inherited\n", " assert config[\"prod\"][\"port\"] == 8080 # Preserved override\n", " assert \"_defaults\" not in config # Cleaned up\n", "\n", "test_config_inheritance()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5. Error Prevention\n", "\n", "#### Validate Paths Before Processing" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def validate_defaults_paths(defaults_config):\n", " \"\"\"Validate _defaults path patterns\"\"\"\n", " for path in defaults_config.keys():\n", " if path.endswith(\"*\"):\n", " raise ValueError(f\"Path cannot end with '*': {path}\")\n", " \n", " if \"..\" in path:\n", " raise ValueError(f\"Path cannot contain '..': {path}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Handle Missing Intermediate Keys" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "# ❌ This will raise KeyError if 'database' doesn't exist\n", "config = {\n", " \"_defaults\": {\"*.database.port\": 5432},\n", " \"dev\": {} # No 'database' key\n", "}\n", "\n", "# ✅ Better: Ensure intermediate structures exist\n", "config = {\n", " \"_defaults\": {\"*.database.port\": 5432},\n", " \"dev\": {\"database\": {}} # Provide empty database config\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "The Hierarchical Configuration Inheritance Pattern solves the fundamental problem of **configuration duplication** in multi-environment applications. By using `_defaults` sections and JSON path patterns, you can:\n", "\n", "### 🎯 **Key Benefits**\n", "- **Eliminate Duplication**: Define common settings once\n", "- **Reduce Errors**: Single source of truth for defaults\n", "- **Scale Easily**: Add new environments with minimal config\n", "- **Override Flexibly**: Keep environment-specific customizations\n", "- **Maintain Simply**: Change defaults in one place\n", "\n", "### 🚀 **When to Use This Pattern**\n", "- ✅ Multi-environment deployments (dev/staging/prod)\n", "- ✅ Microservice configurations with shared defaults\n", "- ✅ Multi-tenant applications with tiered features\n", "- ✅ Configuration templates with customization points\n", "- ✅ Any scenario with repetitive configuration data\n", "\n", "### 🛠️ **Getting Started**\n", "1. Identify duplicated configuration values\n", "2. Extract them to a `_defaults` section \n", "3. Use `*.field` patterns for broad defaults\n", "4. Use `env.field` patterns for specific overrides\n", "5. Call `apply_inheritance()` to process your config\n", "\n", "The pattern transforms configuration management from a maintenance burden into a powerful tool for organizing and scaling your application configurations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.8" } }, "nbformat": 4, "nbformat_minor": 4 }