dataknobs-config 0.1.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 (30) hide show
  1. dataknobs_config-0.1.0/CHANGELOG.md +40 -0
  2. dataknobs_config-0.1.0/PKG-INFO +423 -0
  3. dataknobs_config-0.1.0/README.md +397 -0
  4. dataknobs_config-0.1.0/docs/DESIGN_PLAN.md +165 -0
  5. dataknobs_config-0.1.0/docs/PROGRESS_CHECKLIST.md +70 -0
  6. dataknobs_config-0.1.0/pyproject.toml +55 -0
  7. dataknobs_config-0.1.0/setup.cfg +4 -0
  8. dataknobs_config-0.1.0/src/dataknobs_config/__init__.py +24 -0
  9. dataknobs_config-0.1.0/src/dataknobs_config/builders.py +248 -0
  10. dataknobs_config-0.1.0/src/dataknobs_config/config.py +560 -0
  11. dataknobs_config-0.1.0/src/dataknobs_config/environment.py +213 -0
  12. dataknobs_config-0.1.0/src/dataknobs_config/exceptions.py +37 -0
  13. dataknobs_config-0.1.0/src/dataknobs_config/references.py +170 -0
  14. dataknobs_config-0.1.0/src/dataknobs_config/settings.py +210 -0
  15. dataknobs_config-0.1.0/src/dataknobs_config.egg-info/PKG-INFO +423 -0
  16. dataknobs_config-0.1.0/src/dataknobs_config.egg-info/SOURCES.txt +28 -0
  17. dataknobs_config-0.1.0/src/dataknobs_config.egg-info/dependency_links.txt +1 -0
  18. dataknobs_config-0.1.0/src/dataknobs_config.egg-info/requires.txt +9 -0
  19. dataknobs_config-0.1.0/src/dataknobs_config.egg-info/top_level.txt +1 -0
  20. dataknobs_config-0.1.0/tests/conftest.py +72 -0
  21. dataknobs_config-0.1.0/tests/fixtures/atomic_db.yaml +6 -0
  22. dataknobs_config-0.1.0/tests/fixtures/test_config.json +24 -0
  23. dataknobs_config-0.1.0/tests/fixtures/test_config.yaml +33 -0
  24. dataknobs_config-0.1.0/tests/test_builders.py +259 -0
  25. dataknobs_config-0.1.0/tests/test_config.py +323 -0
  26. dataknobs_config-0.1.0/tests/test_environment.py +203 -0
  27. dataknobs_config-0.1.0/tests/test_lazy_factory.py +260 -0
  28. dataknobs_config-0.1.0/tests/test_path_resolution.py +264 -0
  29. dataknobs_config-0.1.0/tests/test_references.py +217 -0
  30. dataknobs_config-0.1.0/tests/test_regex_path_resolution.py +238 -0
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to the dataknobs-config package will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2025-01-12
9
+
10
+ ### Added
11
+ - Initial release of dataknobs-config package
12
+ - Core `Config` class for managing modular configurations
13
+ - Support for YAML and JSON file formats
14
+ - Atomic configuration management with type/name-based access
15
+ - String reference system (`xref:`) for cross-referencing configurations
16
+ - Environment variable override system with bash-compatible naming
17
+ - Global settings and defaults management
18
+ - Path resolution for relative paths in configurations
19
+ - Object construction support with class instantiation and factory patterns
20
+ - Object caching for improved performance
21
+ - Comprehensive test suite with 91% code coverage
22
+ - Full type annotations with mypy support
23
+ - Detailed documentation and usage examples
24
+
25
+ ### Features
26
+ - **Modular Design**: Organize configurations by type with atomic units
27
+ - **File Loading**: Load from YAML, JSON, or Python dictionaries
28
+ - **Cross-References**: Link configurations using `xref:type[name]` syntax
29
+ - **Environment Overrides**: Override any config value via environment variables
30
+ - **Path Resolution**: Automatic resolution of relative paths
31
+ - **Object Building**: Optional object construction from configurations
32
+ - **Settings Management**: Global and type-specific defaults
33
+ - **Extensible**: Clean interfaces for custom builders and factories
34
+
35
+ ### Technical Details
36
+ - Python 3.8+ support
37
+ - Dependency: PyYAML >= 6.0
38
+ - Development dependencies include pytest, mypy, ruff, and types-PyYAML
39
+ - Follows PEP 8 style guidelines
40
+ - 100% type annotated codebase
@@ -0,0 +1,423 @@
1
+ Metadata-Version: 2.4
2
+ Name: dataknobs-config
3
+ Version: 0.1.0
4
+ Summary: Modular configuration system with composable settings and environment variable overrides
5
+ Author-email: KBS Labs <info@kbs-labs.com>
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: pyyaml>=6.0
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=7.0; extra == "dev"
21
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
22
+ Requires-Dist: black>=22.0; extra == "dev"
23
+ Requires-Dist: flake8>=5.0; extra == "dev"
24
+ Requires-Dist: mypy>=1.0; extra == "dev"
25
+ Requires-Dist: types-PyYAML>=6.0; extra == "dev"
26
+
27
+ # DataKnobs Config
28
+
29
+ A modular, reusable configuration system for composable settings with environment variable overrides, file loading, and optional object construction helpers.
30
+
31
+ ## Features
32
+
33
+ - **Modular Configuration**: Organize configurations by type with atomic configuration units
34
+ - **Multiple Input Formats**: Load from YAML, JSON files, or Python dictionaries
35
+ - **Composable**: Reference other configurations and compose complex setups
36
+ - **Environment Overrides**: Override any configuration value via environment variables
37
+ - **Path Resolution**: Automatically resolve relative paths to absolute
38
+ - **Object Construction**: Optional helpers to build objects from configurations
39
+ - **Defaults Management**: Global and type-specific default values
40
+ - **Caching**: Cache constructed objects for efficiency
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install dataknobs-config
46
+ ```
47
+
48
+ ## Quick Start
49
+
50
+ ```python
51
+ from dataknobs_config import Config
52
+
53
+ # Load from dictionary
54
+ config = Config({
55
+ "database": [
56
+ {"name": "primary", "host": "localhost", "port": 5432},
57
+ {"name": "secondary", "host": "backup.local", "port": 5433}
58
+ ],
59
+ "cache": [
60
+ {"name": "redis", "host": "localhost", "port": 6379}
61
+ ]
62
+ })
63
+
64
+ # Access configurations
65
+ primary_db = config.get("database", "primary")
66
+ print(primary_db["host"]) # localhost
67
+
68
+ # Load from file
69
+ config = Config.from_file("config.yaml")
70
+
71
+ # Load from multiple sources
72
+ config = Config("base.yaml", "overrides.json", {"extra": [...]})
73
+ ```
74
+
75
+ ## Core Concepts
76
+
77
+ ### Atomic Configurations
78
+
79
+ Each configuration is an "atomic" unit - a dictionary of settings for a single object:
80
+
81
+ ```python
82
+ {
83
+ "name": "primary", # Optional, auto-generated if not provided
84
+ "type": "database", # Optional, inferred from parent key
85
+ "host": "localhost",
86
+ "port": 5432,
87
+ # ... any other attributes
88
+ }
89
+ ```
90
+
91
+ ### Configuration Structure
92
+
93
+ Internally, configurations are organized by type:
94
+
95
+ ```python
96
+ {
97
+ "database": [ # Type name
98
+ {...}, # Atomic config 1
99
+ {...} # Atomic config 2
100
+ ],
101
+ "cache": [
102
+ {...} # Atomic config
103
+ ],
104
+ "settings": { # Special type for global settings
105
+ "config_root": "/app/config",
106
+ "default_timeout": 30
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## String References (xref)
112
+
113
+ Reference other configurations using the xref format:
114
+
115
+ ```python
116
+ config = Config({
117
+ "database": [
118
+ {"name": "primary", "host": "db.example.com"}
119
+ ],
120
+ "api": [
121
+ {
122
+ "name": "main",
123
+ "database": "xref:database[primary]" # Reference
124
+ }
125
+ ]
126
+ })
127
+
128
+ # Resolve references
129
+ api = config.resolve_reference("xref:api[main]")
130
+ print(api["database"]["host"]) # db.example.com
131
+ ```
132
+
133
+ ### Reference Formats
134
+
135
+ - `xref:type[name]` - Reference by name
136
+ - `xref:type[0]` - Reference by index
137
+ - `xref:type[-1]` - Reference last item
138
+ - `xref:type` - Reference first/only item
139
+
140
+ ## Environment Variable Overrides
141
+
142
+ Override any configuration value using environment variables:
143
+
144
+ ```bash
145
+ export DATAKNOBS_DATABASE__PRIMARY__HOST=prod.example.com
146
+ export DATAKNOBS_DATABASE__PRIMARY__PORT=5433
147
+ export DATAKNOBS_CACHE__REDIS__TTL=7200
148
+ ```
149
+
150
+ ```python
151
+ config = Config({
152
+ "database": [{"name": "primary", "host": "localhost", "port": 5432}],
153
+ "cache": [{"name": "redis", "ttl": 3600}]
154
+ })
155
+
156
+ # Environment variables automatically override values
157
+ db = config.get("database", "primary")
158
+ print(db["host"]) # prod.example.com
159
+ print(db["port"]) # 5433 (converted to int)
160
+ ```
161
+
162
+ ### Environment Variable Format
163
+
164
+ - Pattern: `DATAKNOBS_<TYPE>__<NAME_OR_INDEX>__<ATTRIBUTE>`
165
+ - Nested attributes: `DATAKNOBS_DATABASE__0__CONNECTION__TIMEOUT`
166
+ - Automatic type conversion for integers, floats, and booleans
167
+
168
+ ## File References
169
+
170
+ Reference external configuration files using the `@` prefix:
171
+
172
+ ```yaml
173
+ # main.yaml
174
+ database:
175
+ - "@database/primary.yaml" # Load from file
176
+ - "@database/secondary.yaml"
177
+
178
+ settings:
179
+ config_root: /app/config # Base path for relative references
180
+ ```
181
+
182
+ ## Global Settings and Defaults
183
+
184
+ Configure global settings and defaults in the special `settings` section:
185
+
186
+ ```python
187
+ config = Config({
188
+ "database": [{"name": "db1"}],
189
+ "settings": {
190
+ # Paths
191
+ "config_root": "/app/config", # Base path for "@"-prefixed config file references
192
+ "global_root": "/app", # Base for path resolution (settings.path_resolution_attributes)
193
+ "database.global_root": "/app/db", # Type-specific base for path resolution
194
+
195
+ # Path resolution (supports exact names and regex patterns)
196
+ "path_resolution_attributes": [
197
+ "config_path", # Exact match for all types
198
+ "database.data_dir", # Exact match for database type only
199
+ "/.*_path$/", # Regex: all attributes ending with "_path"
200
+ "cache./.*_dir$/" # Regex: cache type attributes ending with "_dir"
201
+ ],
202
+
203
+ # Defaults
204
+ "default_timeout": 30, # Global default
205
+ "database.default_pool_size": 10 # Type-specific default
206
+ }
207
+ })
208
+ ```
209
+
210
+ ## Path Resolution
211
+
212
+ Automatically resolve relative paths to absolute:
213
+
214
+ ```python
215
+ config = Config({
216
+ "database": [{
217
+ "name": "db1",
218
+ "data_dir": "./data", # Relative path
219
+ "backup_dir": "/abs/path" # Absolute path unchanged
220
+ }],
221
+ "settings": {
222
+ "global_root": "/app", # Base for path resolution
223
+ "path_resolution_attributes": ["data_dir", "backup_dir"]
224
+ }
225
+ })
226
+
227
+ db = config.get("database", "db1")
228
+ print(db["data_dir"]) # /app/data (resolved)
229
+ print(db["backup_dir"]) # /abs/path (unchanged)
230
+ ```
231
+
232
+ ## Object Construction (Optional)
233
+
234
+ Build objects directly from configurations:
235
+
236
+ ```python
237
+ # Using class attribute
238
+ config = Config({
239
+ "database": [{
240
+ "name": "primary",
241
+ "class": "myapp.database.PostgreSQL",
242
+ "host": "localhost",
243
+ "port": 5432
244
+ }]
245
+ })
246
+
247
+ # Build object
248
+ db = config.build_object("xref:database[primary]")
249
+ # Returns instance of myapp.database.PostgreSQL
250
+
251
+ # Using factory pattern
252
+ config = Config({
253
+ "cache": [{
254
+ "name": "redis",
255
+ "factory": "myapp.cache.CacheFactory",
256
+ "type": "redis",
257
+ "host": "localhost"
258
+ }]
259
+ })
260
+
261
+ cache = config.build_object("xref:cache[redis]")
262
+ ```
263
+
264
+ ### Implementing Configurable Classes
265
+
266
+ ```python
267
+ from dataknobs_config import ConfigurableBase
268
+
269
+ class MyDatabase(ConfigurableBase):
270
+ def __init__(self, host, port, **kwargs):
271
+ self.host = host
272
+ self.port = port
273
+
274
+ @classmethod
275
+ def from_config(cls, config):
276
+ # Custom configuration logic
277
+ return cls(**config)
278
+ ```
279
+
280
+ ### Implementing Factories
281
+
282
+ ```python
283
+ from dataknobs_config import FactoryBase
284
+
285
+ class DatabaseFactory(FactoryBase):
286
+ def create(self, **config):
287
+ db_type = config.pop("type", "postgresql")
288
+ if db_type == "postgresql":
289
+ return PostgreSQL(**config)
290
+ elif db_type == "mysql":
291
+ return MySQL(**config)
292
+ ```
293
+
294
+ ### Lazy Factory Access
295
+
296
+ ```python
297
+ # Configuration with factory
298
+ config = Config({
299
+ "database": [{
300
+ "name": "primary",
301
+ "factory": "myapp.db.DatabaseFactory",
302
+ "type": "postgresql",
303
+ "host": "localhost"
304
+ }]
305
+ })
306
+
307
+ # Get the factory instance (cached)
308
+ factory = config.get_factory("database", "primary")
309
+ db1 = factory.create(database="app1")
310
+ db2 = factory.create(database="app2")
311
+
312
+ # Or get an instance directly
313
+ db = config.get_instance("database", "primary", database="myapp")
314
+ ```
315
+
316
+ ## API Reference
317
+
318
+ ### Config Class
319
+
320
+ ```python
321
+ class Config:
322
+ def __init__(self, *sources, use_env=True)
323
+ def from_file(cls, path) -> Config
324
+ def from_dict(cls, data) -> Config
325
+
326
+ # Access
327
+ def get_types() -> List[str]
328
+ def get_count(type_name: str) -> int
329
+ def get_names(type_name: str) -> List[str]
330
+ def get(type_name: str, name_or_index: Union[str, int] = 0) -> dict
331
+ def set(type_name: str, name_or_index: Union[str, int], config: dict)
332
+
333
+ # References
334
+ def resolve_reference(ref: str) -> dict
335
+ def build_reference(type_name: str, name_or_index: Union[str, int]) -> str
336
+
337
+ # Merging
338
+ def merge(other: Config, precedence: str = "first")
339
+
340
+ # Export
341
+ def to_dict() -> dict
342
+ def to_file(path: Path, format: str = None)
343
+
344
+ # Object Construction
345
+ def build_object(ref: str, cache: bool = True, **kwargs) -> Any
346
+ def clear_object_cache(ref: str = None)
347
+
348
+ # Lazy Factory Access
349
+ def get_factory(type_name: str, name_or_index: Union[str, int] = 0) -> Any
350
+ def get_instance(type_name: str, name_or_index: Union[str, int] = 0, **kwargs) -> Any
351
+ ```
352
+
353
+ ## Examples
354
+
355
+ ### Multi-Environment Configuration
356
+
357
+ ```python
358
+ # base.yaml
359
+ database:
360
+ - name: primary
361
+ host: localhost
362
+ port: 5432
363
+
364
+ # production.yaml
365
+ database:
366
+ - name: primary
367
+ host: prod.db.example.com
368
+ pool_size: 50
369
+
370
+ # Load with overrides
371
+ config = Config("base.yaml", "production.yaml")
372
+ ```
373
+
374
+ ### Service Discovery Integration
375
+
376
+ ```python
377
+ config = Config({
378
+ "services": [
379
+ {"name": "auth", "url": "http://auth:8000"},
380
+ {"name": "api", "url": "http://api:8080"}
381
+ ],
382
+ "app": [{
383
+ "name": "main",
384
+ "auth_service": "xref:services[auth]",
385
+ "api_service": "xref:services[api]"
386
+ }]
387
+ })
388
+
389
+ app = config.resolve_reference("xref:app[main]")
390
+ # app["auth_service"]["url"] = "http://auth:8000"
391
+ ```
392
+
393
+ ### Dynamic Configuration with Environment
394
+
395
+ ```python
396
+ # Development: export DATAKNOBS_DATABASE__PRIMARY__HOST=localhost
397
+ # Production: export DATAKNOBS_DATABASE__PRIMARY__HOST=prod.db.aws.com
398
+
399
+ config = Config.from_file("config.yaml")
400
+ db = config.get("database", "primary")
401
+ # Automatically uses environment-appropriate host
402
+ ```
403
+
404
+ ## Best Practices
405
+
406
+ 1. **Use Type Organization**: Group related configurations by type
407
+ 2. **Leverage Defaults**: Define common values in settings to avoid repetition
408
+ 3. **Environment Overrides**: Use for deployment-specific values (hosts, ports, credentials)
409
+ 4. **File References**: Split large configurations into manageable files
410
+ 5. **Path Resolution**: Use relative paths in configs for portability
411
+ 6. **Object Caching**: Enable caching for expensive object construction
412
+
413
+ ## Testing
414
+
415
+ Run tests with pytest:
416
+
417
+ ```bash
418
+ pytest tests/
419
+ ```
420
+
421
+ ## License
422
+
423
+ MIT License - see LICENSE file for details.