dynamic-config-loader 2.5.0__tar.gz → 2.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {dynamic_config_loader-2.5.0/src/dynamic_config_loader.egg-info → dynamic_config_loader-2.7.0}/PKG-INFO +110 -53
  2. dynamic_config_loader-2.7.0/README.md +153 -0
  3. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/pyproject.toml +1 -1
  4. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/environment.py +11 -4
  5. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/env/parser.py +3 -18
  6. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0/src/dynamic_config_loader.egg-info}/PKG-INFO +110 -53
  7. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_env_injection.py +32 -5
  8. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_env_strategy.py +28 -8
  9. dynamic_config_loader-2.5.0/README.md +0 -96
  10. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/AUTHORS +0 -0
  11. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/LICENSE +0 -0
  12. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/NOTICE +0 -0
  13. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/setup.cfg +0 -0
  14. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/__init__.py +0 -0
  15. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/loader.py +0 -0
  16. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/__init__.py +0 -0
  17. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/base_strategy.py +0 -0
  18. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/env/__init__.py +0 -0
  19. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/env/merger.py +0 -0
  20. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/ini/__init__.py +0 -0
  21. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/ini/merger.py +0 -0
  22. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/ini/parser.py +0 -0
  23. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/json/__init__.py +0 -0
  24. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/json/merger.py +0 -0
  25. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/json/parser.py +0 -0
  26. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/properties/__init__.py +0 -0
  27. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/properties/merger.py +0 -0
  28. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/properties/parser.py +0 -0
  29. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/toml/__init__.py +0 -0
  30. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/toml/merger.py +0 -0
  31. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/toml/parser.py +0 -0
  32. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/xml/__init__.py +0 -0
  33. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/xml/merger.py +0 -0
  34. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/xml/parser.py +0 -0
  35. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/yaml/__init__.py +0 -0
  36. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/yaml/merger.py +0 -0
  37. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader/strategies/yaml/parser.py +0 -0
  38. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader.egg-info/SOURCES.txt +0 -0
  39. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader.egg-info/dependency_links.txt +0 -0
  40. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader.egg-info/requires.txt +0 -0
  41. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/src/dynamic_config_loader.egg-info/top_level.txt +0 -0
  42. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_deep_merge.py +0 -0
  43. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_factory.py +0 -0
  44. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_ini_strategy.py +0 -0
  45. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_json_strategy.py +0 -0
  46. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_loader.py +0 -0
  47. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_logging_safety.py +0 -0
  48. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_properties_strategy.py +0 -0
  49. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_toml_strategy.py +0 -0
  50. {dynamic_config_loader-2.5.0 → dynamic_config_loader-2.7.0}/tests/test_xml_strategy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dynamic-config-loader
3
- Version: 2.5.0
3
+ Version: 2.7.0
4
4
  Summary: An enterprise-grade configuration orchestration engine designed to ingest, cascade, and deeply merge multiple configuration file layers.
5
5
  Author-email: Avinash Kumar <halfhiddencode@gmail.com>
6
6
  License:
@@ -237,51 +237,79 @@ Dynamic: license-file
237
237
 
238
238
  # Dynamic Configuration Loader
239
239
 
240
- A lightweight, production-grade, strategy-driven configuration orchestration engine designed to dynamically discover, alphabetically sort, and recursively deep-merge multi-layered configuration files.
240
+ A lightweight, production-grade, strategy-driven configuration orchestration engine designed to dynamically discover, cascade, and deep-merge multi-layered configuration files.
241
241
 
242
- ## Supported Formats
242
+ ---
243
243
 
244
- The orchestration engine natively parses and cascades the following configuration formats:
245
- * **YAML** (`.yaml`, `.yml`)
246
- * **JSON** (`.json`)
247
- * **XML** (`.xml`)
248
- * **TOML** (`.toml`)
249
- * **INI / Conf** (`.ini`, `.conf`)
250
- * **Java Properties** (`.properties`)
251
- * **Dotenv** (`.env`)
244
+ > [!TIP]
245
+ > ### 👋 NEW TO DYNAMIC CONFIG LOADER? START HERE!
246
+ >
247
+ > Choose your role to find the fastest path to get up and running:
248
+ >
249
+ > * **🆕 Application Developer (First Time User):** Start with the **[3-Line Quick Start Tutorial](docs/tutorials/getting_started.md)**.
250
+ > * **🚀 DevOps / SRE Engineer:** Check the **[Configuration Lifecycle Matrix](docs/guides/user_usage.md#1-minimal-to-advanced-configuration-lifecycle-guide)** & **[K8s Deployment Scenarios](docs/guides/user_usage.md#7-production-deployment-scenarios)**.
251
+ > * **🛠️ Open-Source Contributor:** See the **[Track A vs Track B Contributor Guide](docs/guides/contributor_guide.md#1-contributing-features-vs-format-strategies)**.
252
+ > * **📦 Maintainer / DevOps Release:** Read the **[Release & OIDC Deployment Guide](docs/guides/release_guide.md)**.
252
253
 
253
254
  ---
254
255
 
255
- ```text
256
- Copyright (C) 2026 Avinash Kumar <halfhiddencode AT gmail DOT com>
256
+ ## 📌 Quick Navigation Index
257
257
 
258
- Licensed under the Apache License, Version 2.0 (the "License");
259
- you may not use this file except in compliance with the License.
260
- You may obtain a copy of the License at
258
+ - [⚡ Minimal Quick Start](#-quick-start-minimal-3-lines)
259
+ - [📦 Installation](#-installation)
260
+ - [⚙️ Supported Configuration Formats](#%EF%B8%8F-supported-configuration-formats)
261
+ - [🔀 Multi-Layer Overrides & Environment Injection](#-multi-layer-overrides--environment-injection)
262
+ - [📖 Documentation Index](#-documentation-index)
263
+ - [🎨 System Architecture Visualizer](#-system-architecture)
261
264
 
262
- http://www.apache.org/licenses/LICENSE-2.0
265
+ ---
263
266
 
264
- Unless required by applicable law or agreed to in writing, software
265
- distributed under the License is distributed on an "AS IS" BASIS,
266
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
267
- See the License for the specific language governing permissions and
268
- limitations under the License.
269
- ```
267
+ ### 🚀 Configuration Lifecycle Matrix: Minimal to Advanced
268
+
269
+ | Lifecycle Phase | Component Requirement | How It Works in Code / Environment | Primary Purpose |
270
+ | :--- | :--- | :--- | :--- |
271
+ | **1. Local Dev (Coding Time)** | `[OPTIONAL FALLBACK]` | `loader = DynamicConfigLoader("config.yaml")` | Local fallback file for offline developer workstations. |
272
+ | **2. Image Build (CI/CD Build Time)** | `[REQUIRED IN CODE]` | `loader = DynamicConfigLoader()` | **Image Immutability!** Zero hardcoded secrets in Docker image. |
273
+ | **3. Deployment (K8s Runtime Time)** | `[OPTIONAL AT DEPLOYMENT]` | `export PYTHON_ADDITIONAL_CONFIG="/etc/config/prod.yaml"` | Mounts K8s ConfigMaps & Vault secrets into container at runtime. |
274
+ | **4. Live Ops Overrides (Runtime)** | `[OPTIONAL AT RUNTIME]` | `export DCL_ENV_LOADER=true` <br> `export DCL_PORT=8080` | Overrides specific keys in memory dynamically without rebuilding image. |
270
275
 
271
276
  ---
272
277
 
273
- ## Project Metadata
274
- * **Maintainer:** Avinash Kumar
275
- * **Target Ecosystem:** Python 3.8+ / GitLab Source Hosting and PyPI Package Registry
276
- * **Official Repository:** [python-dynamic-config-loader](https://gitlab.com/halfhiddencode/python-dynamic-config-loader)
277
- * **Licensing:** Apache License, Version 2.0
278
- * **Core Design Patterns:** Strategy Pattern, Registry Factory
278
+ ### Minimal Execution Code Snippets
279
+
280
+ #### Mode 1: Container / Kubernetes Mode (`[RECOMMENDED FOR PROD]`)
281
+
282
+ > [!NOTE]
283
+ > **[OPTIONAL FILE PATH - CAN SKIP!]**
284
+ > If you don't have a local config file, **YOU CAN SKIP IT!** In production container deployments, you do NOT need any hardcoded file paths in your code. The engine will handle configuration loading automatically at deployment time via environment variables (`PYTHON_ADDITIONAL_CONFIG` or `DCL_`).
285
+
286
+ ```python
287
+ from dynamic_config_loader import DynamicConfigLoader
288
+
289
+ # Zero hardcoded file paths! Fully handled at deployment time
290
+ loader = DynamicConfigLoader()
291
+ config = loader.load()
292
+ ```
293
+
294
+ #### Mode 2: Local Development Mode (`[OPTIONAL FALLBACK]`)
295
+
296
+ > [!NOTE]
297
+ > **[LOCAL FALLBACK ONLY]**
298
+ > Useful for local developer laptops so you can run `python app.py` offline without configuring environment variables.
299
+
300
+ ```python
301
+ from dynamic_config_loader import DynamicConfigLoader
302
+
303
+ # Optional local fallback file
304
+ loader = DynamicConfigLoader("config.yaml")
305
+ config = loader.load()
306
+ ```
279
307
 
280
308
  ---
281
309
 
282
- ## Installation
310
+ ## 📦 Installation
283
311
 
284
- Install the package directly from PyPI:
312
+ Install from PyPI via `pip`:
285
313
 
286
314
  ```bash
287
315
  pip install dynamic-config-loader
@@ -295,39 +323,68 @@ pip install -e .
295
323
 
296
324
  ---
297
325
 
298
- ## Quick Start
326
+ ## ⚙️ Supported Configuration Formats
299
327
 
300
- ```python
301
- import os
302
- from dynamic_config_loader import DynamicConfigLoader
328
+ The orchestration engine natively ingests, parses, and cascades **7 configuration formats**:
303
329
 
304
- # 1. (Optional) Provide path override layers via system environment
305
- os.environ["PYTHON_ADDITIONAL_CONFIG"] = "/app/configs/overrides_directory"
330
+ | Format | Extensions | Key Behavior & Conversion | Guide Link |
331
+ | :--- | :--- | :--- | :--- |
332
+ | **YAML** | `.yaml`, `.yml` | Native YAML indentation & deep dictionary parsing | [View Details](docs/guides/user_usage.md#71-yaml-strategy-yamlstrategy) |
333
+ | **JSON** | `.json` | Strict JSON objects & typed arrays | [View Details](docs/guides/user_usage.md#72-json-strategy-jsonstrategy) |
334
+ | **Dotenv** | `.env` | **100% Flat Keys** (`SERVICE_GATEWAY_NAME` $\rightarrow$ `config["service_gateway_name"]`) | [View Details](docs/guides/user_usage.md#87-dotenv-strategy-envstrategy) |
335
+ | **TOML** | `.toml` | Section headers (`[table]`), inline tables, & datetimes | [View Details](docs/guides/user_usage.md#74-toml-strategy-tomlstrategy) |
336
+ | **INI / Conf** | `.ini`, `.conf` | Section headers (`[section]`) & automatic primitive type casting | [View Details](docs/guides/user_usage.md#75-ini--conf-strategy-inistrategy) |
337
+ | **Java Properties**| `.properties` | Dot notation (`app.database.port=5432` $\rightarrow$ `app.database.port`) | [View Details](docs/guides/user_usage.md#76-java-properties-strategy-propertiesstrategy) |
338
+ | **XML** | `.xml` | Tag element hierarchies auto-converted into Python dicts | [View Details](docs/guides/user_usage.md#73-xml-strategy-xmlstrategy) |
306
339
 
307
- # 2. Instantiate with a local base configuration file
308
- loader = DynamicConfigLoader(default_config_path="/app/configs/base_defaults.yaml")
340
+ ---
309
341
 
310
- # 3. Load and compile configurations
311
- config = loader.load()
312
- print(config["database"]["host"])
342
+ ## 🔀 Multi-Layer Overrides & Environment Injection
343
+
344
+ <details>
345
+ <summary><b>Click to expand Advanced Environment Overrides & Logging Options</b></summary>
346
+
347
+ ### 1. External File/Directory Overrides (`PYTHON_ADDITIONAL_CONFIG`)
348
+ Specify an external file or directory of override files via system environment variables:
349
+
350
+ ```bash
351
+ export PYTHON_ADDITIONAL_CONFIG="/etc/app/production_overrides.yaml"
313
352
  ```
314
353
 
354
+ ### 2. Environment Variable Injection (`DCL_ENV_LOADER=true`)
355
+ Inject or override specific keys dynamically at runtime using environment variables:
356
+
357
+ ```bash
358
+ export DCL_ENV_LOADER=true
359
+ export DCL_PORT=9000
360
+ export DCL_LOG_LEVEL=info
361
+ ```
362
+
363
+ ### 3. Log Redaction Controls (`ENABLE_UNSAFE_LOGGING`)
364
+ * **Default (Safe Mode):** Sensitive passwords and tokens are redacted (`[REDACTED]`) in logs.
365
+ * **Unsafe Mode (`ENABLE_UNSAFE_LOGGING=true`):** Exposes raw values during local debug sessions.
366
+
367
+ </details>
368
+
315
369
  ---
316
370
 
317
- ## Documentation Index
371
+ ## 🎨 System Architecture
372
+
373
+ ![Master Architecture Pipeline & Deep Merge Visualizer](docs/images/master_architecture_deep_merge.svg)
374
+
375
+ ---
318
376
 
319
- The complete enterprise documentation is organized under the Diátaxis framework layout rules within the `docs/` directory. No emojis are used in the index or guidelines.
377
+ ## 📖 Documentation Index
320
378
 
321
- | Track | Target Document | Description |
379
+ | Guide Track | Link | Description |
322
380
  | :--- | :--- | :--- |
323
- | **Getting Started** | [Getting Started Tutorial](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/tutorials/getting_started.md) | Onboarding sequence, repeatable format tracks, and installation via GitLab Package Registry. |
324
- | **Operations Guide** | [Advanced Ingestion and Path Traversal Guide](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/guides/user_usage.md) | Path parsing matrix, edge-case refusals, Central Config Manager pattern, and log masking safety details. |
325
- | **Contributor Guide** | [Format Strategy Contribution Guide](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/guides/contributor_guide.md) | Abstract base strategy class signatures, factory registration hooks, and copy-pasteable JSON blueprints. |
326
- | **Technical Reference** | [Core Architecture Reference](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/reference/architecture.md) | System design architecture, UML mapping, execution pipelines, and logging safety compliance matrices. |
381
+ | **🚀 Getting Started Tutorial** | [getting_started.md](docs/tutorials/getting_started.md) | Step-by-step onboarding & minimal execution tracks. |
382
+ | **📖 Operations & Usage Guide** | [user_usage.md](docs/guides/user_usage.md) | Complete format matrix, K8s/DevOps deployment scenarios, & path edge cases. |
383
+ | **🛠️ Contributor Guide** | [contributor_guide.md](docs/guides/contributor_guide.md) | Adding custom format strategies & factory registry hooks. |
384
+ | **🚀 Release & OIDC Guide** | [release_guide.md](docs/guides/release_guide.md) | OIDC Trusted Publishing, version bump rules, & PyPI deployment protocols. |
385
+ | **🏗️ Architecture Reference** | [architecture.md](docs/reference/architecture.md) | System design, UML diagrams, & deep merge algorithms. |
327
386
 
328
387
  ---
329
388
 
330
- ## Repository Ledgers
331
- * Version History: [CHANGELOG.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/CHANGELOG.md)
332
- * Security Policy: [SECURITY.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/SECURITY.md)
333
- * Contribution Protocols: [CONTRIBUTING.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/CONTRIBUTING.md)
389
+ ## 📜 License
390
+ Licensed under the [Apache License, Version 2.0](LICENSE).
@@ -0,0 +1,153 @@
1
+ # Dynamic Configuration Loader
2
+
3
+ A lightweight, production-grade, strategy-driven configuration orchestration engine designed to dynamically discover, cascade, and deep-merge multi-layered configuration files.
4
+
5
+ ---
6
+
7
+ > [!TIP]
8
+ > ### 👋 NEW TO DYNAMIC CONFIG LOADER? START HERE!
9
+ >
10
+ > Choose your role to find the fastest path to get up and running:
11
+ >
12
+ > * **🆕 Application Developer (First Time User):** Start with the **[3-Line Quick Start Tutorial](docs/tutorials/getting_started.md)**.
13
+ > * **🚀 DevOps / SRE Engineer:** Check the **[Configuration Lifecycle Matrix](docs/guides/user_usage.md#1-minimal-to-advanced-configuration-lifecycle-guide)** & **[K8s Deployment Scenarios](docs/guides/user_usage.md#7-production-deployment-scenarios)**.
14
+ > * **🛠️ Open-Source Contributor:** See the **[Track A vs Track B Contributor Guide](docs/guides/contributor_guide.md#1-contributing-features-vs-format-strategies)**.
15
+ > * **📦 Maintainer / DevOps Release:** Read the **[Release & OIDC Deployment Guide](docs/guides/release_guide.md)**.
16
+
17
+ ---
18
+
19
+ ## 📌 Quick Navigation Index
20
+
21
+ - [⚡ Minimal Quick Start](#-quick-start-minimal-3-lines)
22
+ - [📦 Installation](#-installation)
23
+ - [⚙️ Supported Configuration Formats](#%EF%B8%8F-supported-configuration-formats)
24
+ - [🔀 Multi-Layer Overrides & Environment Injection](#-multi-layer-overrides--environment-injection)
25
+ - [📖 Documentation Index](#-documentation-index)
26
+ - [🎨 System Architecture Visualizer](#-system-architecture)
27
+
28
+ ---
29
+
30
+ ### 🚀 Configuration Lifecycle Matrix: Minimal to Advanced
31
+
32
+ | Lifecycle Phase | Component Requirement | How It Works in Code / Environment | Primary Purpose |
33
+ | :--- | :--- | :--- | :--- |
34
+ | **1. Local Dev (Coding Time)** | `[OPTIONAL FALLBACK]` | `loader = DynamicConfigLoader("config.yaml")` | Local fallback file for offline developer workstations. |
35
+ | **2. Image Build (CI/CD Build Time)** | `[REQUIRED IN CODE]` | `loader = DynamicConfigLoader()` | **Image Immutability!** Zero hardcoded secrets in Docker image. |
36
+ | **3. Deployment (K8s Runtime Time)** | `[OPTIONAL AT DEPLOYMENT]` | `export PYTHON_ADDITIONAL_CONFIG="/etc/config/prod.yaml"` | Mounts K8s ConfigMaps & Vault secrets into container at runtime. |
37
+ | **4. Live Ops Overrides (Runtime)** | `[OPTIONAL AT RUNTIME]` | `export DCL_ENV_LOADER=true` <br> `export DCL_PORT=8080` | Overrides specific keys in memory dynamically without rebuilding image. |
38
+
39
+ ---
40
+
41
+ ### Minimal Execution Code Snippets
42
+
43
+ #### Mode 1: Container / Kubernetes Mode (`[RECOMMENDED FOR PROD]`)
44
+
45
+ > [!NOTE]
46
+ > **[OPTIONAL FILE PATH - CAN SKIP!]**
47
+ > If you don't have a local config file, **YOU CAN SKIP IT!** In production container deployments, you do NOT need any hardcoded file paths in your code. The engine will handle configuration loading automatically at deployment time via environment variables (`PYTHON_ADDITIONAL_CONFIG` or `DCL_`).
48
+
49
+ ```python
50
+ from dynamic_config_loader import DynamicConfigLoader
51
+
52
+ # Zero hardcoded file paths! Fully handled at deployment time
53
+ loader = DynamicConfigLoader()
54
+ config = loader.load()
55
+ ```
56
+
57
+ #### Mode 2: Local Development Mode (`[OPTIONAL FALLBACK]`)
58
+
59
+ > [!NOTE]
60
+ > **[LOCAL FALLBACK ONLY]**
61
+ > Useful for local developer laptops so you can run `python app.py` offline without configuring environment variables.
62
+
63
+ ```python
64
+ from dynamic_config_loader import DynamicConfigLoader
65
+
66
+ # Optional local fallback file
67
+ loader = DynamicConfigLoader("config.yaml")
68
+ config = loader.load()
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 📦 Installation
74
+
75
+ Install from PyPI via `pip`:
76
+
77
+ ```bash
78
+ pip install dynamic-config-loader
79
+ ```
80
+
81
+ Or install in editable mode for local development:
82
+
83
+ ```bash
84
+ pip install -e .
85
+ ```
86
+
87
+ ---
88
+
89
+ ## ⚙️ Supported Configuration Formats
90
+
91
+ The orchestration engine natively ingests, parses, and cascades **7 configuration formats**:
92
+
93
+ | Format | Extensions | Key Behavior & Conversion | Guide Link |
94
+ | :--- | :--- | :--- | :--- |
95
+ | **YAML** | `.yaml`, `.yml` | Native YAML indentation & deep dictionary parsing | [View Details](docs/guides/user_usage.md#71-yaml-strategy-yamlstrategy) |
96
+ | **JSON** | `.json` | Strict JSON objects & typed arrays | [View Details](docs/guides/user_usage.md#72-json-strategy-jsonstrategy) |
97
+ | **Dotenv** | `.env` | **100% Flat Keys** (`SERVICE_GATEWAY_NAME` $\rightarrow$ `config["service_gateway_name"]`) | [View Details](docs/guides/user_usage.md#87-dotenv-strategy-envstrategy) |
98
+ | **TOML** | `.toml` | Section headers (`[table]`), inline tables, & datetimes | [View Details](docs/guides/user_usage.md#74-toml-strategy-tomlstrategy) |
99
+ | **INI / Conf** | `.ini`, `.conf` | Section headers (`[section]`) & automatic primitive type casting | [View Details](docs/guides/user_usage.md#75-ini--conf-strategy-inistrategy) |
100
+ | **Java Properties**| `.properties` | Dot notation (`app.database.port=5432` $\rightarrow$ `app.database.port`) | [View Details](docs/guides/user_usage.md#76-java-properties-strategy-propertiesstrategy) |
101
+ | **XML** | `.xml` | Tag element hierarchies auto-converted into Python dicts | [View Details](docs/guides/user_usage.md#73-xml-strategy-xmlstrategy) |
102
+
103
+ ---
104
+
105
+ ## 🔀 Multi-Layer Overrides & Environment Injection
106
+
107
+ <details>
108
+ <summary><b>Click to expand Advanced Environment Overrides & Logging Options</b></summary>
109
+
110
+ ### 1. External File/Directory Overrides (`PYTHON_ADDITIONAL_CONFIG`)
111
+ Specify an external file or directory of override files via system environment variables:
112
+
113
+ ```bash
114
+ export PYTHON_ADDITIONAL_CONFIG="/etc/app/production_overrides.yaml"
115
+ ```
116
+
117
+ ### 2. Environment Variable Injection (`DCL_ENV_LOADER=true`)
118
+ Inject or override specific keys dynamically at runtime using environment variables:
119
+
120
+ ```bash
121
+ export DCL_ENV_LOADER=true
122
+ export DCL_PORT=9000
123
+ export DCL_LOG_LEVEL=info
124
+ ```
125
+
126
+ ### 3. Log Redaction Controls (`ENABLE_UNSAFE_LOGGING`)
127
+ * **Default (Safe Mode):** Sensitive passwords and tokens are redacted (`[REDACTED]`) in logs.
128
+ * **Unsafe Mode (`ENABLE_UNSAFE_LOGGING=true`):** Exposes raw values during local debug sessions.
129
+
130
+ </details>
131
+
132
+ ---
133
+
134
+ ## 🎨 System Architecture
135
+
136
+ ![Master Architecture Pipeline & Deep Merge Visualizer](docs/images/master_architecture_deep_merge.svg)
137
+
138
+ ---
139
+
140
+ ## 📖 Documentation Index
141
+
142
+ | Guide Track | Link | Description |
143
+ | :--- | :--- | :--- |
144
+ | **🚀 Getting Started Tutorial** | [getting_started.md](docs/tutorials/getting_started.md) | Step-by-step onboarding & minimal execution tracks. |
145
+ | **📖 Operations & Usage Guide** | [user_usage.md](docs/guides/user_usage.md) | Complete format matrix, K8s/DevOps deployment scenarios, & path edge cases. |
146
+ | **🛠️ Contributor Guide** | [contributor_guide.md](docs/guides/contributor_guide.md) | Adding custom format strategies & factory registry hooks. |
147
+ | **🚀 Release & OIDC Guide** | [release_guide.md](docs/guides/release_guide.md) | OIDC Trusted Publishing, version bump rules, & PyPI deployment protocols. |
148
+ | **🏗️ Architecture Reference** | [architecture.md](docs/reference/architecture.md) | System design, UML diagrams, & deep merge algorithms. |
149
+
150
+ ---
151
+
152
+ ## 📜 License
153
+ Licensed under the [Apache License, Version 2.0](LICENSE).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dynamic-config-loader"
7
- version = "2.5.0"
7
+ version = "2.7.0"
8
8
  description = "An enterprise-grade configuration orchestration engine designed to ingest, cascade, and deeply merge multiple configuration file layers."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -56,11 +56,18 @@ def inject_environment_variables(
56
56
  if env_key.startswith(prefix):
57
57
  # Strip prefix
58
58
  sliced_key = env_key[len(prefix):]
59
- if not sliced_key:
60
- continue
59
+ parsed_val = _parse_env_value(env_val)
60
+
61
+ # Resolution order:
62
+ # 1. Explicit double-underscore '__': Splits into nested objects (e.g. DCL_AUTH__TENANT_ID -> config_data["auth"]["tenant_id"])
63
+ # 2. Default: Preserves as 100% FLAT key (e.g. DCL_SERVICE_GATEWAY_NAME -> config_data["service_gateway_name"])
64
+ flat_key = sliced_key.strip().lower()
65
+
66
+ if "__" in sliced_key:
67
+ parts = [p.lower() for p in sliced_key.split("__") if p]
68
+ else:
69
+ parts = [flat_key]
61
70
 
62
- # Case Normalization and tokenizing
63
- parts = [p.lower() for p in sliced_key.split("_") if p]
64
71
  if not parts:
65
72
  continue
66
73
 
@@ -46,15 +46,11 @@ def parse_env_file(file_path: Path) -> Dict[str, Any]:
46
46
  with open(file_path, "r", encoding="utf-8") as f:
47
47
  for line in f:
48
48
  line = line.strip()
49
- # Ignore empty lines and comment lines starting with '#'
50
- if not line or line.startswith("#"):
51
- continue
52
-
53
- if "=" not in line:
49
+ if not line or line.startswith("#") or line.startswith("!") or "=" not in line:
54
50
  continue
55
51
 
56
52
  key, val = line.split("=", 1)
57
- key = key.strip()
53
+ key = key.strip().lower()
58
54
  val = val.strip()
59
55
 
60
56
  if not key:
@@ -67,18 +63,7 @@ def parse_env_file(file_path: Path) -> Dict[str, Any]:
67
63
  val = val[1:-1]
68
64
 
69
65
  parsed_val = _parse_env_file_value(val)
70
-
71
- # Tokenize key by single underscore delimiter to align with env injection track
72
- parts = [p.lower() for p in key.split("_") if p]
73
- if not parts:
74
- continue
75
-
76
- curr = data
77
- for part in parts[:-1]:
78
- if part not in curr or not isinstance(curr[part], dict):
79
- curr[part] = {}
80
- curr = curr[part]
81
- curr[parts[-1]] = parsed_val
66
+ data[key] = parsed_val
82
67
 
83
68
  except (IOError, PermissionError) as e:
84
69
  logger.exception(f"Failed to read or parse dotenv file: {file_path.name}", exc_info=e)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dynamic-config-loader
3
- Version: 2.5.0
3
+ Version: 2.7.0
4
4
  Summary: An enterprise-grade configuration orchestration engine designed to ingest, cascade, and deeply merge multiple configuration file layers.
5
5
  Author-email: Avinash Kumar <halfhiddencode@gmail.com>
6
6
  License:
@@ -237,51 +237,79 @@ Dynamic: license-file
237
237
 
238
238
  # Dynamic Configuration Loader
239
239
 
240
- A lightweight, production-grade, strategy-driven configuration orchestration engine designed to dynamically discover, alphabetically sort, and recursively deep-merge multi-layered configuration files.
240
+ A lightweight, production-grade, strategy-driven configuration orchestration engine designed to dynamically discover, cascade, and deep-merge multi-layered configuration files.
241
241
 
242
- ## Supported Formats
242
+ ---
243
243
 
244
- The orchestration engine natively parses and cascades the following configuration formats:
245
- * **YAML** (`.yaml`, `.yml`)
246
- * **JSON** (`.json`)
247
- * **XML** (`.xml`)
248
- * **TOML** (`.toml`)
249
- * **INI / Conf** (`.ini`, `.conf`)
250
- * **Java Properties** (`.properties`)
251
- * **Dotenv** (`.env`)
244
+ > [!TIP]
245
+ > ### 👋 NEW TO DYNAMIC CONFIG LOADER? START HERE!
246
+ >
247
+ > Choose your role to find the fastest path to get up and running:
248
+ >
249
+ > * **🆕 Application Developer (First Time User):** Start with the **[3-Line Quick Start Tutorial](docs/tutorials/getting_started.md)**.
250
+ > * **🚀 DevOps / SRE Engineer:** Check the **[Configuration Lifecycle Matrix](docs/guides/user_usage.md#1-minimal-to-advanced-configuration-lifecycle-guide)** & **[K8s Deployment Scenarios](docs/guides/user_usage.md#7-production-deployment-scenarios)**.
251
+ > * **🛠️ Open-Source Contributor:** See the **[Track A vs Track B Contributor Guide](docs/guides/contributor_guide.md#1-contributing-features-vs-format-strategies)**.
252
+ > * **📦 Maintainer / DevOps Release:** Read the **[Release & OIDC Deployment Guide](docs/guides/release_guide.md)**.
252
253
 
253
254
  ---
254
255
 
255
- ```text
256
- Copyright (C) 2026 Avinash Kumar <halfhiddencode AT gmail DOT com>
256
+ ## 📌 Quick Navigation Index
257
257
 
258
- Licensed under the Apache License, Version 2.0 (the "License");
259
- you may not use this file except in compliance with the License.
260
- You may obtain a copy of the License at
258
+ - [⚡ Minimal Quick Start](#-quick-start-minimal-3-lines)
259
+ - [📦 Installation](#-installation)
260
+ - [⚙️ Supported Configuration Formats](#%EF%B8%8F-supported-configuration-formats)
261
+ - [🔀 Multi-Layer Overrides & Environment Injection](#-multi-layer-overrides--environment-injection)
262
+ - [📖 Documentation Index](#-documentation-index)
263
+ - [🎨 System Architecture Visualizer](#-system-architecture)
261
264
 
262
- http://www.apache.org/licenses/LICENSE-2.0
265
+ ---
263
266
 
264
- Unless required by applicable law or agreed to in writing, software
265
- distributed under the License is distributed on an "AS IS" BASIS,
266
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
267
- See the License for the specific language governing permissions and
268
- limitations under the License.
269
- ```
267
+ ### 🚀 Configuration Lifecycle Matrix: Minimal to Advanced
268
+
269
+ | Lifecycle Phase | Component Requirement | How It Works in Code / Environment | Primary Purpose |
270
+ | :--- | :--- | :--- | :--- |
271
+ | **1. Local Dev (Coding Time)** | `[OPTIONAL FALLBACK]` | `loader = DynamicConfigLoader("config.yaml")` | Local fallback file for offline developer workstations. |
272
+ | **2. Image Build (CI/CD Build Time)** | `[REQUIRED IN CODE]` | `loader = DynamicConfigLoader()` | **Image Immutability!** Zero hardcoded secrets in Docker image. |
273
+ | **3. Deployment (K8s Runtime Time)** | `[OPTIONAL AT DEPLOYMENT]` | `export PYTHON_ADDITIONAL_CONFIG="/etc/config/prod.yaml"` | Mounts K8s ConfigMaps & Vault secrets into container at runtime. |
274
+ | **4. Live Ops Overrides (Runtime)** | `[OPTIONAL AT RUNTIME]` | `export DCL_ENV_LOADER=true` <br> `export DCL_PORT=8080` | Overrides specific keys in memory dynamically without rebuilding image. |
270
275
 
271
276
  ---
272
277
 
273
- ## Project Metadata
274
- * **Maintainer:** Avinash Kumar
275
- * **Target Ecosystem:** Python 3.8+ / GitLab Source Hosting and PyPI Package Registry
276
- * **Official Repository:** [python-dynamic-config-loader](https://gitlab.com/halfhiddencode/python-dynamic-config-loader)
277
- * **Licensing:** Apache License, Version 2.0
278
- * **Core Design Patterns:** Strategy Pattern, Registry Factory
278
+ ### Minimal Execution Code Snippets
279
+
280
+ #### Mode 1: Container / Kubernetes Mode (`[RECOMMENDED FOR PROD]`)
281
+
282
+ > [!NOTE]
283
+ > **[OPTIONAL FILE PATH - CAN SKIP!]**
284
+ > If you don't have a local config file, **YOU CAN SKIP IT!** In production container deployments, you do NOT need any hardcoded file paths in your code. The engine will handle configuration loading automatically at deployment time via environment variables (`PYTHON_ADDITIONAL_CONFIG` or `DCL_`).
285
+
286
+ ```python
287
+ from dynamic_config_loader import DynamicConfigLoader
288
+
289
+ # Zero hardcoded file paths! Fully handled at deployment time
290
+ loader = DynamicConfigLoader()
291
+ config = loader.load()
292
+ ```
293
+
294
+ #### Mode 2: Local Development Mode (`[OPTIONAL FALLBACK]`)
295
+
296
+ > [!NOTE]
297
+ > **[LOCAL FALLBACK ONLY]**
298
+ > Useful for local developer laptops so you can run `python app.py` offline without configuring environment variables.
299
+
300
+ ```python
301
+ from dynamic_config_loader import DynamicConfigLoader
302
+
303
+ # Optional local fallback file
304
+ loader = DynamicConfigLoader("config.yaml")
305
+ config = loader.load()
306
+ ```
279
307
 
280
308
  ---
281
309
 
282
- ## Installation
310
+ ## 📦 Installation
283
311
 
284
- Install the package directly from PyPI:
312
+ Install from PyPI via `pip`:
285
313
 
286
314
  ```bash
287
315
  pip install dynamic-config-loader
@@ -295,39 +323,68 @@ pip install -e .
295
323
 
296
324
  ---
297
325
 
298
- ## Quick Start
326
+ ## ⚙️ Supported Configuration Formats
299
327
 
300
- ```python
301
- import os
302
- from dynamic_config_loader import DynamicConfigLoader
328
+ The orchestration engine natively ingests, parses, and cascades **7 configuration formats**:
303
329
 
304
- # 1. (Optional) Provide path override layers via system environment
305
- os.environ["PYTHON_ADDITIONAL_CONFIG"] = "/app/configs/overrides_directory"
330
+ | Format | Extensions | Key Behavior & Conversion | Guide Link |
331
+ | :--- | :--- | :--- | :--- |
332
+ | **YAML** | `.yaml`, `.yml` | Native YAML indentation & deep dictionary parsing | [View Details](docs/guides/user_usage.md#71-yaml-strategy-yamlstrategy) |
333
+ | **JSON** | `.json` | Strict JSON objects & typed arrays | [View Details](docs/guides/user_usage.md#72-json-strategy-jsonstrategy) |
334
+ | **Dotenv** | `.env` | **100% Flat Keys** (`SERVICE_GATEWAY_NAME` $\rightarrow$ `config["service_gateway_name"]`) | [View Details](docs/guides/user_usage.md#87-dotenv-strategy-envstrategy) |
335
+ | **TOML** | `.toml` | Section headers (`[table]`), inline tables, & datetimes | [View Details](docs/guides/user_usage.md#74-toml-strategy-tomlstrategy) |
336
+ | **INI / Conf** | `.ini`, `.conf` | Section headers (`[section]`) & automatic primitive type casting | [View Details](docs/guides/user_usage.md#75-ini--conf-strategy-inistrategy) |
337
+ | **Java Properties**| `.properties` | Dot notation (`app.database.port=5432` $\rightarrow$ `app.database.port`) | [View Details](docs/guides/user_usage.md#76-java-properties-strategy-propertiesstrategy) |
338
+ | **XML** | `.xml` | Tag element hierarchies auto-converted into Python dicts | [View Details](docs/guides/user_usage.md#73-xml-strategy-xmlstrategy) |
306
339
 
307
- # 2. Instantiate with a local base configuration file
308
- loader = DynamicConfigLoader(default_config_path="/app/configs/base_defaults.yaml")
340
+ ---
309
341
 
310
- # 3. Load and compile configurations
311
- config = loader.load()
312
- print(config["database"]["host"])
342
+ ## 🔀 Multi-Layer Overrides & Environment Injection
343
+
344
+ <details>
345
+ <summary><b>Click to expand Advanced Environment Overrides & Logging Options</b></summary>
346
+
347
+ ### 1. External File/Directory Overrides (`PYTHON_ADDITIONAL_CONFIG`)
348
+ Specify an external file or directory of override files via system environment variables:
349
+
350
+ ```bash
351
+ export PYTHON_ADDITIONAL_CONFIG="/etc/app/production_overrides.yaml"
313
352
  ```
314
353
 
354
+ ### 2. Environment Variable Injection (`DCL_ENV_LOADER=true`)
355
+ Inject or override specific keys dynamically at runtime using environment variables:
356
+
357
+ ```bash
358
+ export DCL_ENV_LOADER=true
359
+ export DCL_PORT=9000
360
+ export DCL_LOG_LEVEL=info
361
+ ```
362
+
363
+ ### 3. Log Redaction Controls (`ENABLE_UNSAFE_LOGGING`)
364
+ * **Default (Safe Mode):** Sensitive passwords and tokens are redacted (`[REDACTED]`) in logs.
365
+ * **Unsafe Mode (`ENABLE_UNSAFE_LOGGING=true`):** Exposes raw values during local debug sessions.
366
+
367
+ </details>
368
+
315
369
  ---
316
370
 
317
- ## Documentation Index
371
+ ## 🎨 System Architecture
372
+
373
+ ![Master Architecture Pipeline & Deep Merge Visualizer](docs/images/master_architecture_deep_merge.svg)
374
+
375
+ ---
318
376
 
319
- The complete enterprise documentation is organized under the Diátaxis framework layout rules within the `docs/` directory. No emojis are used in the index or guidelines.
377
+ ## 📖 Documentation Index
320
378
 
321
- | Track | Target Document | Description |
379
+ | Guide Track | Link | Description |
322
380
  | :--- | :--- | :--- |
323
- | **Getting Started** | [Getting Started Tutorial](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/tutorials/getting_started.md) | Onboarding sequence, repeatable format tracks, and installation via GitLab Package Registry. |
324
- | **Operations Guide** | [Advanced Ingestion and Path Traversal Guide](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/guides/user_usage.md) | Path parsing matrix, edge-case refusals, Central Config Manager pattern, and log masking safety details. |
325
- | **Contributor Guide** | [Format Strategy Contribution Guide](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/guides/contributor_guide.md) | Abstract base strategy class signatures, factory registration hooks, and copy-pasteable JSON blueprints. |
326
- | **Technical Reference** | [Core Architecture Reference](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/reference/architecture.md) | System design architecture, UML mapping, execution pipelines, and logging safety compliance matrices. |
381
+ | **🚀 Getting Started Tutorial** | [getting_started.md](docs/tutorials/getting_started.md) | Step-by-step onboarding & minimal execution tracks. |
382
+ | **📖 Operations & Usage Guide** | [user_usage.md](docs/guides/user_usage.md) | Complete format matrix, K8s/DevOps deployment scenarios, & path edge cases. |
383
+ | **🛠️ Contributor Guide** | [contributor_guide.md](docs/guides/contributor_guide.md) | Adding custom format strategies & factory registry hooks. |
384
+ | **🚀 Release & OIDC Guide** | [release_guide.md](docs/guides/release_guide.md) | OIDC Trusted Publishing, version bump rules, & PyPI deployment protocols. |
385
+ | **🏗️ Architecture Reference** | [architecture.md](docs/reference/architecture.md) | System design, UML diagrams, & deep merge algorithms. |
327
386
 
328
387
  ---
329
388
 
330
- ## Repository Ledgers
331
- * Version History: [CHANGELOG.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/CHANGELOG.md)
332
- * Security Policy: [SECURITY.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/SECURITY.md)
333
- * Contribution Protocols: [CONTRIBUTING.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/CONTRIBUTING.md)
389
+ ## 📜 License
390
+ Licensed under the [Apache License, Version 2.0](LICENSE).
@@ -39,9 +39,9 @@ class TestEnvInjection(unittest.TestCase):
39
39
  Verifies that configuration parameters are injected correctly with the default prefix.
40
40
  """
41
41
  os.environ["DCL_ENV_LOADER"] = "true"
42
- os.environ["DCL_DATABASE_PORT"] = "9000"
43
- os.environ["DCL_DATABASE_USER"] = "postgres_env"
44
- os.environ["DCL_NEW_FEATURE_ENABLED"] = "true"
42
+ os.environ["DCL_DATABASE__PORT"] = "9000"
43
+ os.environ["DCL_DATABASE__USER"] = "postgres_env"
44
+ os.environ["DCL_SERVICE_GATEWAY_NAME"] = "payment_gateway"
45
45
 
46
46
  loader = DynamicConfigLoader(default_config_path=str(self.base_file))
47
47
  result = loader.load()
@@ -49,14 +49,41 @@ class TestEnvInjection(unittest.TestCase):
49
49
  # Check nested structures are created or overridden
50
50
  self.assertEqual(result["database"]["port"], 9000) # String "9000" is parsed to int 9000
51
51
  self.assertEqual(result["database"]["user"], "postgres_env")
52
- self.assertTrue(result["new"]["feature"]["enabled"]) # String "true" is parsed to bool True
52
+ self.assertEqual(result["service_gateway_name"], "payment_gateway") # Single _ is 100% flat!
53
+
54
+ def test_injection_double_underscore_delimiter(self):
55
+ """
56
+ Verifies that double-underscore '__' preserves single underscores in injected key names.
57
+ """
58
+ os.environ["DCL_ENV_LOADER"] = "true"
59
+ os.environ["DCL_AUTH__TENANT_ID"] = "5fa1dea3-2af8-4cd5"
60
+ os.environ["DCL_AWS_S3__BUCKET_NAME"] = "my-bucket-prod"
61
+
62
+ loader = DynamicConfigLoader(default_config_path=str(self.base_file))
63
+ result = loader.load()
64
+
65
+ self.assertEqual(result["auth"]["tenant_id"], "5fa1dea3-2af8-4cd5")
66
+ self.assertEqual(result["aws_s3"]["bucket_name"], "my-bucket-prod")
67
+
68
+ def test_injection_flat_key_override(self):
69
+ """
70
+ Verifies that DCL_APP_NAME cleanly overrides a flat key 'app_name' from baseline.
71
+ """
72
+ os.environ["DCL_ENV_LOADER"] = "true"
73
+ os.environ["DCL_APP_NAME"] = "Overridden Gateway App"
74
+
75
+ env_file = self.TEST_DIR / "test_configs" / "base_config.env"
76
+ loader = DynamicConfigLoader(default_config_path=str(env_file))
77
+ result = loader.load()
78
+
79
+ self.assertEqual(result["app_name"], "Overridden Gateway App")
53
80
 
54
81
  def test_injection_custom_prefix(self):
55
82
  """
56
83
  Verifies that configuration parameters are injected correctly using a custom prefix.
57
84
  """
58
85
  os.environ["DCL_ENV_LOADER"] = "true"
59
- os.environ["MYAPP_DB_HOST"] = "remote.db"
86
+ os.environ["MYAPP_DB__HOST"] = "remote.db"
60
87
 
61
88
  # Pass custom prefix to constructor
62
89
  loader = DynamicConfigLoader(default_config_path=str(self.base_file), prefix="MYAPP_")
@@ -36,10 +36,30 @@ class TestEnvStrategy(unittest.TestCase):
36
36
  Verifies that parse_env_file correctly parses env variables and translates primitive types.
37
37
  """
38
38
  data = parse_env_file(self.base_file)
39
- self.assertEqual(data["app"]["name"], "Test Env Gateway")
40
- self.assertFalse(data["debug"]["mode"])
41
- self.assertEqual(data["database"]["port"], 5432)
42
- self.assertEqual(data["database"]["host"], "127.0.0.1")
39
+ self.assertEqual(data["app_name"], "Test Env Gateway")
40
+ self.assertFalse(data["debug_mode"])
41
+ self.assertEqual(data["database_port"], 5432)
42
+ self.assertEqual(data["database_host"], "127.0.0.1")
43
+
44
+ def test_flat_env_key_parsing(self):
45
+ """
46
+ Verifies that dotenv files return 100% flat dictionary mappings without creating nested sub-dictionaries.
47
+ """
48
+ import tempfile
49
+ with tempfile.NamedTemporaryFile("w+", suffix=".env", delete=False) as f:
50
+ f.write("AUTH__TENANT_ID=5fa1dea3\n")
51
+ f.write("AWS_S3__BUCKET_NAME=my-bucket\n")
52
+ f.write("SERVICE_GATEWAY_NAME=my_app\n")
53
+ temp_path = Path(f.name)
54
+
55
+ try:
56
+ data = parse_env_file(temp_path)
57
+ self.assertEqual(data["auth__tenant_id"], "5fa1dea3")
58
+ self.assertEqual(data["aws_s3__bucket_name"], "my-bucket")
59
+ self.assertEqual(data["service_gateway_name"], "my_app")
60
+ finally:
61
+ if temp_path.exists():
62
+ temp_path.unlink()
43
63
 
44
64
  def test_env_deep_merging(self):
45
65
  """
@@ -80,10 +100,10 @@ class TestEnvStrategy(unittest.TestCase):
80
100
  loader = DynamicConfigLoader(default_config_path=str(self.base_file))
81
101
  result = loader.load()
82
102
 
83
- self.assertEqual(result["app"]["name"], "Test Env Gateway")
84
- self.assertTrue(result["debug"]["mode"])
85
- self.assertEqual(result["database"]["host"], "env-dev-cluster.local")
86
- self.assertEqual(result["database"]["port"], 5432)
103
+ self.assertEqual(result["app_name"], "Test Env Gateway")
104
+ self.assertTrue(result["debug_mode"])
105
+ self.assertEqual(result["database_host"], "env-dev-cluster.local")
106
+ self.assertEqual(result["database_port"], 5432)
87
107
 
88
108
  def test_env_log_masking(self):
89
109
  """
@@ -1,96 +0,0 @@
1
- # Dynamic Configuration Loader
2
-
3
- A lightweight, production-grade, strategy-driven configuration orchestration engine designed to dynamically discover, alphabetically sort, and recursively deep-merge multi-layered configuration files.
4
-
5
- ## Supported Formats
6
-
7
- The orchestration engine natively parses and cascades the following configuration formats:
8
- * **YAML** (`.yaml`, `.yml`)
9
- * **JSON** (`.json`)
10
- * **XML** (`.xml`)
11
- * **TOML** (`.toml`)
12
- * **INI / Conf** (`.ini`, `.conf`)
13
- * **Java Properties** (`.properties`)
14
- * **Dotenv** (`.env`)
15
-
16
- ---
17
-
18
- ```text
19
- Copyright (C) 2026 Avinash Kumar <halfhiddencode AT gmail DOT com>
20
-
21
- Licensed under the Apache License, Version 2.0 (the "License");
22
- you may not use this file except in compliance with the License.
23
- You may obtain a copy of the License at
24
-
25
- http://www.apache.org/licenses/LICENSE-2.0
26
-
27
- Unless required by applicable law or agreed to in writing, software
28
- distributed under the License is distributed on an "AS IS" BASIS,
29
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30
- See the License for the specific language governing permissions and
31
- limitations under the License.
32
- ```
33
-
34
- ---
35
-
36
- ## Project Metadata
37
- * **Maintainer:** Avinash Kumar
38
- * **Target Ecosystem:** Python 3.8+ / GitLab Source Hosting and PyPI Package Registry
39
- * **Official Repository:** [python-dynamic-config-loader](https://gitlab.com/halfhiddencode/python-dynamic-config-loader)
40
- * **Licensing:** Apache License, Version 2.0
41
- * **Core Design Patterns:** Strategy Pattern, Registry Factory
42
-
43
- ---
44
-
45
- ## Installation
46
-
47
- Install the package directly from PyPI:
48
-
49
- ```bash
50
- pip install dynamic-config-loader
51
- ```
52
-
53
- Or install in editable mode for local development:
54
-
55
- ```bash
56
- pip install -e .
57
- ```
58
-
59
- ---
60
-
61
- ## Quick Start
62
-
63
- ```python
64
- import os
65
- from dynamic_config_loader import DynamicConfigLoader
66
-
67
- # 1. (Optional) Provide path override layers via system environment
68
- os.environ["PYTHON_ADDITIONAL_CONFIG"] = "/app/configs/overrides_directory"
69
-
70
- # 2. Instantiate with a local base configuration file
71
- loader = DynamicConfigLoader(default_config_path="/app/configs/base_defaults.yaml")
72
-
73
- # 3. Load and compile configurations
74
- config = loader.load()
75
- print(config["database"]["host"])
76
- ```
77
-
78
- ---
79
-
80
- ## Documentation Index
81
-
82
- The complete enterprise documentation is organized under the Diátaxis framework layout rules within the `docs/` directory. No emojis are used in the index or guidelines.
83
-
84
- | Track | Target Document | Description |
85
- | :--- | :--- | :--- |
86
- | **Getting Started** | [Getting Started Tutorial](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/tutorials/getting_started.md) | Onboarding sequence, repeatable format tracks, and installation via GitLab Package Registry. |
87
- | **Operations Guide** | [Advanced Ingestion and Path Traversal Guide](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/guides/user_usage.md) | Path parsing matrix, edge-case refusals, Central Config Manager pattern, and log masking safety details. |
88
- | **Contributor Guide** | [Format Strategy Contribution Guide](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/guides/contributor_guide.md) | Abstract base strategy class signatures, factory registration hooks, and copy-pasteable JSON blueprints. |
89
- | **Technical Reference** | [Core Architecture Reference](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/docs/reference/architecture.md) | System design architecture, UML mapping, execution pipelines, and logging safety compliance matrices. |
90
-
91
- ---
92
-
93
- ## Repository Ledgers
94
- * Version History: [CHANGELOG.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/CHANGELOG.md)
95
- * Security Policy: [SECURITY.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/SECURITY.md)
96
- * Contribution Protocols: [CONTRIBUTING.md](https://gitlab.com/halfhiddencode/python-dynamic-config-loader/-/blob/main/CONTRIBUTING.md)