jsonconfig-utils 1.0.3__py3-none-any.whl
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.
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jsonconfig-utils
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: Lightweight JSON configuration loader with environment variable interpolation
|
|
5
|
+
Home-page: https://github.com/config-utils/jsonconfig-utils
|
|
6
|
+
Author: Config Utils Contributors
|
|
7
|
+
Author-email: config-utils-py@proton.me
|
|
8
|
+
Keywords: json config configuration env loader
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Requires-Python: >=3.7
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: author-email
|
|
18
|
+
Dynamic: classifier
|
|
19
|
+
Dynamic: description
|
|
20
|
+
Dynamic: description-content-type
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: keywords
|
|
23
|
+
Dynamic: requires-python
|
|
24
|
+
Dynamic: summary
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# jsonconfig-utils
|
|
28
|
+
|
|
29
|
+
Simple JSON config loader with env var interpolation, schema validation, and nested key access.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
```
|
|
33
|
+
pip install jsonconfig-utils
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
```python
|
|
38
|
+
from jsonconfig_utils import load_config
|
|
39
|
+
config = load_config("config.json")
|
|
40
|
+
print(config.database.host)
|
|
41
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
jsonconfig_utils.py,sha256=aEIS-OmU9ys-OGWWQb28h_tzwSrwqlnAMRTylK3qtX4,2008
|
|
2
|
+
jsonconfig_utils-1.0.3.dist-info/METADATA,sha256=JlOMJ4kBszkQkRk8gG3LjmKKqj7ZHCpOQ2mi6mHjb_8,1144
|
|
3
|
+
jsonconfig_utils-1.0.3.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
4
|
+
jsonconfig_utils-1.0.3.dist-info/top_level.txt,sha256=Ugw0U-2soo52K13Cz09vLOkjSgCrXKxTgit7v5cVPPs,17
|
|
5
|
+
jsonconfig_utils-1.0.3.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jsonconfig_utils
|
jsonconfig_utils.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""jsonconfig-utils: Lightweight JSON configuration loader."""
|
|
2
|
+
|
|
3
|
+
__version__ = "1.0.3"
|
|
4
|
+
__all__ = ["load_config", "ConfigDict", "validate_schema"]
|
|
5
|
+
|
|
6
|
+
import json, os, re
|
|
7
|
+
|
|
8
|
+
def load_config(path, env_interpolate=True, defaults=None):
|
|
9
|
+
with open(path) as f:
|
|
10
|
+
data = json.load(f)
|
|
11
|
+
if defaults:
|
|
12
|
+
data = _merge(defaults, data)
|
|
13
|
+
if env_interpolate:
|
|
14
|
+
data = _interpolate_env(data)
|
|
15
|
+
return ConfigDict(data)
|
|
16
|
+
|
|
17
|
+
def validate_schema(data, schema):
|
|
18
|
+
errors = []
|
|
19
|
+
for key, expected_type in schema.items():
|
|
20
|
+
if key not in data:
|
|
21
|
+
errors.append(f"Missing required key: {key}")
|
|
22
|
+
elif not isinstance(data[key], expected_type):
|
|
23
|
+
errors.append(f"Key \'{key}\' expected {expected_type.__name__}, got {type(data[key]).__name__}")
|
|
24
|
+
return errors
|
|
25
|
+
|
|
26
|
+
class ConfigDict(dict):
|
|
27
|
+
def __getattr__(self, key):
|
|
28
|
+
try:
|
|
29
|
+
val = self[key]
|
|
30
|
+
return ConfigDict(val) if isinstance(val, dict) else val
|
|
31
|
+
except KeyError:
|
|
32
|
+
raise AttributeError(f"Config has no key \'{key}\'")
|
|
33
|
+
def __setattr__(self, key, value):
|
|
34
|
+
self[key] = value
|
|
35
|
+
def get_nested(self, dotted_key, default=None):
|
|
36
|
+
keys = dotted_key.split(".")
|
|
37
|
+
val = self
|
|
38
|
+
for k in keys:
|
|
39
|
+
if isinstance(val, dict) and k in val:
|
|
40
|
+
val = val[k]
|
|
41
|
+
else:
|
|
42
|
+
return default
|
|
43
|
+
return val
|
|
44
|
+
|
|
45
|
+
def _interpolate_env(obj):
|
|
46
|
+
if isinstance(obj, str):
|
|
47
|
+
return re.sub(r"\$\{(\w+)\}", lambda m: os.environ.get(m.group(1), m.group(0)), obj)
|
|
48
|
+
elif isinstance(obj, dict):
|
|
49
|
+
return {k: _interpolate_env(v) for k, v in obj.items()}
|
|
50
|
+
elif isinstance(obj, list):
|
|
51
|
+
return [_interpolate_env(v) for v in obj]
|
|
52
|
+
return obj
|
|
53
|
+
|
|
54
|
+
def _merge(base, override):
|
|
55
|
+
result = base.copy()
|
|
56
|
+
for k, v in override.items():
|
|
57
|
+
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
|
|
58
|
+
result[k] = _merge(result[k], v)
|
|
59
|
+
else:
|
|
60
|
+
result[k] = v
|
|
61
|
+
return result
|