python-simpleconf 0.7.2__tar.gz → 0.8.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.
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/PKG-INFO +30 -13
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/README.md +23 -9
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/pyproject.toml +9 -5
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/__init__.py +1 -1
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/config.py +203 -4
- python_simpleconf-0.8.0/simpleconf/loaders/__init__.py +134 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/loaders/dict.py +4 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/loaders/env.py +23 -11
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/loaders/ini.py +31 -16
- python_simpleconf-0.8.0/simpleconf/loaders/json.py +47 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/loaders/osenv.py +15 -11
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/loaders/toml.py +22 -1
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/loaders/yaml.py +20 -1
- python_simpleconf-0.7.2/simpleconf/loaders/__init__.py +0 -63
- python_simpleconf-0.7.2/simpleconf/loaders/json.py +0 -28
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/LICENSE +0 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/caster.py +0 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/exceptions.py +0 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/py.typed +0 -0
- {python_simpleconf-0.7.2 → python_simpleconf-0.8.0}/simpleconf/utils.py +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-simpleconf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Simple configuration management with python.
|
|
5
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Author: pwwang
|
|
7
8
|
Author-email: pwwang@pwwang.com
|
|
8
9
|
Requires-Python: >=3.9,<4.0
|
|
@@ -13,19 +14,21 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
18
|
Provides-Extra: all
|
|
17
|
-
Provides-Extra:
|
|
19
|
+
Provides-Extra: async
|
|
18
20
|
Provides-Extra: env
|
|
19
21
|
Provides-Extra: ini
|
|
20
22
|
Provides-Extra: toml
|
|
21
23
|
Provides-Extra: yaml
|
|
24
|
+
Requires-Dist: aiofiles (>=23.0.0) ; extra == "async"
|
|
22
25
|
Requires-Dist: diot (>=0.3.2,<0.4.0)
|
|
23
26
|
Requires-Dist: iniconfig (>=2.0,<3.0) ; extra == "ini" or extra == "all"
|
|
27
|
+
Requires-Dist: panpath (>=0.4.8,<0.5.0)
|
|
24
28
|
Requires-Dist: python-dotenv (>=1.1,<2.0) ; extra == "env" or extra == "all"
|
|
25
29
|
Requires-Dist: pyyaml (>=6,<7) ; extra == "yaml" or extra == "all"
|
|
26
30
|
Requires-Dist: rtoml (>=0.12,<0.13) ; (sys_platform == "linux") and (extra == "toml" or extra == "all")
|
|
27
31
|
Requires-Dist: tomli (>=2.0,<3.0) ; (sys_platform != "linux") and (extra == "toml" or extra == "all")
|
|
28
|
-
Requires-Dist: yunpath (>=0.0.4,<0.0.5) ; extra == "cloud"
|
|
29
32
|
Project-URL: Homepage, https://github.com/pwwang/simpleconf
|
|
30
33
|
Project-URL: Repository, https://github.com/pwwang/simpleconf
|
|
31
34
|
Description-Content-Type: text/markdown
|
|
@@ -60,24 +63,34 @@ pip install python-simpleconf[all]
|
|
|
60
63
|
- Type casting
|
|
61
64
|
- Profile support
|
|
62
65
|
- Simple APIs
|
|
66
|
+
- Async loading support
|
|
63
67
|
|
|
64
68
|
## Usage
|
|
65
69
|
|
|
66
70
|
### Loading configurations
|
|
67
71
|
|
|
68
72
|
```python
|
|
73
|
+
import asyncio
|
|
69
74
|
from simpleconf import Config
|
|
70
75
|
|
|
71
|
-
# Load a single file
|
|
72
|
-
conf = Config.load('~/xxx.ini')
|
|
73
|
-
# load multiple files, later files override previous ones
|
|
74
|
-
conf = Config.load(
|
|
75
|
-
'~/xxx.ini', '~/xxx.env', '~/xxx.yaml', '~/xxx.toml',
|
|
76
|
-
'~/xxx.json', 'simpleconf.osenv', {'a': 3}
|
|
77
|
-
)
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
async def main():
|
|
78
|
+
# Load a single file
|
|
79
|
+
conf = Config.load('~/xxx.ini')
|
|
80
|
+
# load multiple files, later files override previous ones
|
|
81
|
+
conf = Config.load(
|
|
82
|
+
'~/xxx.ini', '~/xxx.env', '~/xxx.yaml', '~/xxx.toml',
|
|
83
|
+
'~/xxx.json', 'simpleconf.osenv', {'a': 3}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Load a single file with a different loader
|
|
87
|
+
conf = Config.load('~/xxx.ini', loader="toml")
|
|
88
|
+
|
|
89
|
+
# Async loading
|
|
90
|
+
conf = await Config.a_load('~/xxx.ini')
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
asyncio.run(main())
|
|
81
94
|
```
|
|
82
95
|
|
|
83
96
|
### Accessing configuration values
|
|
@@ -115,6 +128,10 @@ from simpleconf import ProfileConfig
|
|
|
115
128
|
|
|
116
129
|
conf = ProfileConfig.load({'default': {'a': 1})
|
|
117
130
|
# conf.a == 1
|
|
131
|
+
|
|
132
|
+
# Asynchronous loading
|
|
133
|
+
# conf = await ProfileConfig.a_load({'default': {'a': 1})
|
|
134
|
+
# conf.a == 1
|
|
118
135
|
```
|
|
119
136
|
|
|
120
137
|
##### Loading a `.env` file
|
|
@@ -28,24 +28,34 @@ pip install python-simpleconf[all]
|
|
|
28
28
|
- Type casting
|
|
29
29
|
- Profile support
|
|
30
30
|
- Simple APIs
|
|
31
|
+
- Async loading support
|
|
31
32
|
|
|
32
33
|
## Usage
|
|
33
34
|
|
|
34
35
|
### Loading configurations
|
|
35
36
|
|
|
36
37
|
```python
|
|
38
|
+
import asyncio
|
|
37
39
|
from simpleconf import Config
|
|
38
40
|
|
|
39
|
-
# Load a single file
|
|
40
|
-
conf = Config.load('~/xxx.ini')
|
|
41
|
-
# load multiple files, later files override previous ones
|
|
42
|
-
conf = Config.load(
|
|
43
|
-
'~/xxx.ini', '~/xxx.env', '~/xxx.yaml', '~/xxx.toml',
|
|
44
|
-
'~/xxx.json', 'simpleconf.osenv', {'a': 3}
|
|
45
|
-
)
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
async def main():
|
|
43
|
+
# Load a single file
|
|
44
|
+
conf = Config.load('~/xxx.ini')
|
|
45
|
+
# load multiple files, later files override previous ones
|
|
46
|
+
conf = Config.load(
|
|
47
|
+
'~/xxx.ini', '~/xxx.env', '~/xxx.yaml', '~/xxx.toml',
|
|
48
|
+
'~/xxx.json', 'simpleconf.osenv', {'a': 3}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Load a single file with a different loader
|
|
52
|
+
conf = Config.load('~/xxx.ini', loader="toml")
|
|
53
|
+
|
|
54
|
+
# Async loading
|
|
55
|
+
conf = await Config.a_load('~/xxx.ini')
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
asyncio.run(main())
|
|
49
59
|
```
|
|
50
60
|
|
|
51
61
|
### Accessing configuration values
|
|
@@ -83,6 +93,10 @@ from simpleconf import ProfileConfig
|
|
|
83
93
|
|
|
84
94
|
conf = ProfileConfig.load({'default': {'a': 1})
|
|
85
95
|
# conf.a == 1
|
|
96
|
+
|
|
97
|
+
# Asynchronous loading
|
|
98
|
+
# conf = await ProfileConfig.a_load({'default': {'a': 1})
|
|
99
|
+
# conf.a == 1
|
|
86
100
|
```
|
|
87
101
|
|
|
88
102
|
##### Loading a `.env` file
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "python-simpleconf"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "Simple configuration management with python."
|
|
9
9
|
authors = [ "pwwang <pwwang@pwwang.com>",]
|
|
10
10
|
license = "MIT"
|
|
@@ -20,33 +20,37 @@ python = "^3.9"
|
|
|
20
20
|
diot = "^0.3.2"
|
|
21
21
|
python-dotenv = { version="^1.1", optional = true}
|
|
22
22
|
pyyaml = { version="^6", optional = true}
|
|
23
|
+
panpath = "^0.4.8"
|
|
23
24
|
# Use rtoml only when the wheel is available (linux)
|
|
24
25
|
rtoml = {version = "^0.12", optional = true, platform = "linux"}
|
|
25
26
|
tomli = {version = "^2.0", optional = true, markers = 'sys_platform != "linux"'}
|
|
26
27
|
iniconfig = {version = "^2.0", optional = true}
|
|
27
|
-
|
|
28
|
+
aiofiles = {version = ">=23.0.0", optional = true}
|
|
28
29
|
|
|
29
30
|
[tool.poetry.extras]
|
|
30
31
|
ini = [ "iniconfig" ]
|
|
31
32
|
env = [ "python-dotenv"]
|
|
32
33
|
yaml = [ "pyyaml"]
|
|
33
34
|
toml = [ "rtoml", "tomli"]
|
|
35
|
+
async = [ "aiofiles"]
|
|
34
36
|
all = [ "iniconfig", "python-dotenv", "pyyaml", "rtoml", "tomli"]
|
|
35
|
-
cloud = [ "yunpath"]
|
|
36
37
|
|
|
37
38
|
[tool.poetry.group.dev.dependencies]
|
|
38
39
|
pytest = "^8"
|
|
39
40
|
pytest-cov = "^6"
|
|
41
|
+
pytest-asyncio = "^1"
|
|
42
|
+
backports-asyncio-runner = {version = "^1.1.0", python = "<3.11"}
|
|
40
43
|
|
|
41
44
|
[tool.pytest.ini_options]
|
|
42
|
-
addopts = "-vv
|
|
45
|
+
addopts = "-vv --cov=simpleconf --cov-report xml:cov.xml --cov-report term-missing"
|
|
43
46
|
filterwarnings = [
|
|
44
47
|
"error"
|
|
45
48
|
]
|
|
46
49
|
console_output_style = "progress"
|
|
47
50
|
junit_family = "xunit1"
|
|
51
|
+
asyncio_mode = "auto"
|
|
48
52
|
|
|
49
53
|
[tool.black]
|
|
50
54
|
line-length = 88
|
|
51
|
-
target-version = ['
|
|
55
|
+
target-version = ['py39', 'py310', 'py311', 'py312']
|
|
52
56
|
include = '\.pyi?$'
|
|
@@ -14,8 +14,9 @@ LoaderType = Union[str, Loader, None]
|
|
|
14
14
|
class Config:
|
|
15
15
|
"""The configuration class"""
|
|
16
16
|
|
|
17
|
-
@
|
|
17
|
+
@classmethod
|
|
18
18
|
def load(
|
|
19
|
+
cls,
|
|
19
20
|
*configs: Any,
|
|
20
21
|
loader: LoaderType | Sequence[LoaderType] = None,
|
|
21
22
|
ignore_nonexist: bool = False,
|
|
@@ -50,8 +51,51 @@ class Config:
|
|
|
50
51
|
|
|
51
52
|
return out
|
|
52
53
|
|
|
53
|
-
@
|
|
54
|
+
@classmethod
|
|
55
|
+
async def a_load(
|
|
56
|
+
cls,
|
|
57
|
+
*configs: Any,
|
|
58
|
+
loader: LoaderType | Sequence[LoaderType] = None,
|
|
59
|
+
ignore_nonexist: bool = False,
|
|
60
|
+
) -> Diot:
|
|
61
|
+
"""Asynchronously load the configuration from the files, or other
|
|
62
|
+
configurations
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
*configs: The configuration files or other configurations to load
|
|
66
|
+
Latter ones will override the former ones for items with the
|
|
67
|
+
same keys recursively.
|
|
68
|
+
loader: The loader to use. If a list is given, it must have the
|
|
69
|
+
same length as configs.
|
|
70
|
+
ignore_nonexist: Whether to ignore non-existent files
|
|
71
|
+
Otherwise, will raise errors
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
A Diot object with the loaded configurations
|
|
75
|
+
"""
|
|
76
|
+
if not isinstance(loader, Sequence) or isinstance(loader, str):
|
|
77
|
+
loader = [loader] * len(configs)
|
|
78
|
+
|
|
79
|
+
if len(loader) != len(configs):
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Length of loader ({len(loader)}) does not match "
|
|
82
|
+
f"length of configs ({len(configs)})"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
out = Diot()
|
|
86
|
+
for i, conf in enumerate(configs):
|
|
87
|
+
loaded = await cls.a_load_one(
|
|
88
|
+
conf,
|
|
89
|
+
loader[i],
|
|
90
|
+
ignore_nonexist,
|
|
91
|
+
)
|
|
92
|
+
out.update_recursively(loaded)
|
|
93
|
+
|
|
94
|
+
return out
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
54
97
|
def load_one(
|
|
98
|
+
cls,
|
|
55
99
|
config,
|
|
56
100
|
loader: str | Loader | None = None,
|
|
57
101
|
ignore_nonexist: bool = False,
|
|
@@ -78,12 +122,42 @@ class Config:
|
|
|
78
122
|
|
|
79
123
|
return loader.load(config, ignore_nonexist)
|
|
80
124
|
|
|
125
|
+
@classmethod
|
|
126
|
+
async def a_load_one(
|
|
127
|
+
cls,
|
|
128
|
+
config,
|
|
129
|
+
loader: str | Loader | None = None,
|
|
130
|
+
ignore_nonexist: bool = False,
|
|
131
|
+
) -> Diot:
|
|
132
|
+
"""Asynchronously load the configuration from the file
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
config: The configuration file to load
|
|
136
|
+
loader: The loader to use
|
|
137
|
+
ignore_nonexist: Whether to ignore non-existent files
|
|
138
|
+
Otherwise, will raise errors
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
A Diot object with the loaded configuration
|
|
142
|
+
"""
|
|
143
|
+
if loader is None:
|
|
144
|
+
if hasattr(config, "read"):
|
|
145
|
+
raise ValueError("'loader' must be specified for stream")
|
|
146
|
+
|
|
147
|
+
ext = config_to_ext(config)
|
|
148
|
+
loader = get_loader(ext)
|
|
149
|
+
else:
|
|
150
|
+
loader = get_loader(loader)
|
|
151
|
+
|
|
152
|
+
return await loader.a_load(config, ignore_nonexist)
|
|
153
|
+
|
|
81
154
|
|
|
82
155
|
class ProfileConfig:
|
|
83
156
|
"""The configuration class with profile support"""
|
|
84
157
|
|
|
85
|
-
@
|
|
158
|
+
@classmethod
|
|
86
159
|
def load(
|
|
160
|
+
cls,
|
|
87
161
|
*configs: Any,
|
|
88
162
|
loader: LoaderType | Sequence[LoaderType] = None,
|
|
89
163
|
ignore_nonexist: bool = False,
|
|
@@ -146,8 +220,78 @@ class ProfileConfig:
|
|
|
146
220
|
|
|
147
221
|
return out
|
|
148
222
|
|
|
149
|
-
@
|
|
223
|
+
@classmethod
|
|
224
|
+
async def a_load(
|
|
225
|
+
cls,
|
|
226
|
+
*configs: Any,
|
|
227
|
+
loader: LoaderType | Sequence[LoaderType] = None,
|
|
228
|
+
ignore_nonexist: bool = False,
|
|
229
|
+
base: str = "default",
|
|
230
|
+
allow_missing_base: bool = False,
|
|
231
|
+
) -> Diot:
|
|
232
|
+
"""Asynchronously load the configuration from the files, or other
|
|
233
|
+
configurations
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
*configs: The configuration files or other configurations to load
|
|
237
|
+
Latter ones will override the former ones for items with the
|
|
238
|
+
same profile and keys recursively.
|
|
239
|
+
loader: The loader to use. If a list is given, it must have the
|
|
240
|
+
same length as configs.
|
|
241
|
+
ignore_nonexist: Whether to ignore non-existent files
|
|
242
|
+
Otherwise, will raise errors
|
|
243
|
+
base: The default profile to use after loading
|
|
244
|
+
allow_missing_base: Whether to allow missing base profile
|
|
245
|
+
If False, will raise errors when the base profile is not found
|
|
246
|
+
in the loaded profiles.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
A Diot object with the loaded configurations
|
|
250
|
+
"""
|
|
251
|
+
if not isinstance(loader, Sequence) or isinstance(loader, str):
|
|
252
|
+
loader = [loader] * len(configs)
|
|
253
|
+
|
|
254
|
+
if len(loader) != len(configs):
|
|
255
|
+
raise ValueError(
|
|
256
|
+
f"Length of loader ({len(loader)}) does not match "
|
|
257
|
+
f"length of configs ({len(configs)})"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
out = Diot({POOL_KEY: Diot()})
|
|
261
|
+
pool = out[POOL_KEY]
|
|
262
|
+
out[META_KEY] = {
|
|
263
|
+
"current_profile": None,
|
|
264
|
+
"base_profile": None,
|
|
265
|
+
}
|
|
266
|
+
for i, conf in enumerate(configs):
|
|
267
|
+
lder = loader[i]
|
|
268
|
+
|
|
269
|
+
if lder is None and hasattr(conf, "read"):
|
|
270
|
+
raise ValueError("'loader' must be specified for stream")
|
|
271
|
+
|
|
272
|
+
if lder is None:
|
|
273
|
+
ext = config_to_ext(conf)
|
|
274
|
+
lder = get_loader(ext)
|
|
275
|
+
else:
|
|
276
|
+
lder = get_loader(lder)
|
|
277
|
+
|
|
278
|
+
loaded = await lder.a_load_with_profiles(conf, ignore_nonexist)
|
|
279
|
+
for profile, value in loaded.items():
|
|
280
|
+
profile = profile.lower()
|
|
281
|
+
pool.setdefault(profile, Diot())
|
|
282
|
+
pool[profile].update_recursively(value)
|
|
283
|
+
|
|
284
|
+
if base and base not in pool and not allow_missing_base:
|
|
285
|
+
raise ValueError(f"Base profile '{base}' not found")
|
|
286
|
+
|
|
287
|
+
if base and base in pool:
|
|
288
|
+
out = ProfileConfig.use_profile(out, base, base=base)
|
|
289
|
+
|
|
290
|
+
return out
|
|
291
|
+
|
|
292
|
+
@classmethod
|
|
150
293
|
def load_one(
|
|
294
|
+
cls,
|
|
151
295
|
conf: Any,
|
|
152
296
|
loader: str | Loader | None = None,
|
|
153
297
|
ignore_nonexist: bool = False,
|
|
@@ -197,6 +341,61 @@ class ProfileConfig:
|
|
|
197
341
|
|
|
198
342
|
return out
|
|
199
343
|
|
|
344
|
+
@classmethod
|
|
345
|
+
async def a_load_one(
|
|
346
|
+
cls,
|
|
347
|
+
conf: Any,
|
|
348
|
+
loader: str | Loader | None = None,
|
|
349
|
+
ignore_nonexist: bool = False,
|
|
350
|
+
base: str = "default",
|
|
351
|
+
allow_missing_base: bool = False,
|
|
352
|
+
) -> Diot:
|
|
353
|
+
"""Asynchronously load the configuration from the file
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
conf: The configuration file to load
|
|
357
|
+
loader: The loader to use. Will detect from conf by default
|
|
358
|
+
ignore_nonexist: Whether to ignore non-existent files
|
|
359
|
+
Otherwise, will raise errors
|
|
360
|
+
base: The default profile to use after loading
|
|
361
|
+
allow_missing_base: Whether to allow missing base profile
|
|
362
|
+
If False, will raise errors when the base profile is not found
|
|
363
|
+
in the loaded profiles.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
A Diot object with the loaded configuration
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
out = Diot({POOL_KEY: Diot()})
|
|
370
|
+
pool = out[POOL_KEY]
|
|
371
|
+
out[META_KEY] = {
|
|
372
|
+
"current_profile": None,
|
|
373
|
+
"base_profile": None,
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if loader is None:
|
|
377
|
+
if hasattr(conf, "read"):
|
|
378
|
+
raise ValueError("'loader' must be specified for stream")
|
|
379
|
+
|
|
380
|
+
ext = config_to_ext(conf)
|
|
381
|
+
loader = get_loader(ext)
|
|
382
|
+
else:
|
|
383
|
+
loader = get_loader(loader)
|
|
384
|
+
|
|
385
|
+
loaded = await loader.a_load_with_profiles(conf, ignore_nonexist)
|
|
386
|
+
for profile, value in loaded.items():
|
|
387
|
+
profile = profile.lower()
|
|
388
|
+
pool.setdefault(profile, Diot())
|
|
389
|
+
pool[profile].update_recursively(value)
|
|
390
|
+
|
|
391
|
+
if base and base not in pool and not allow_missing_base:
|
|
392
|
+
raise ValueError(f"Base profile '{base}' not found")
|
|
393
|
+
|
|
394
|
+
if base and base in pool:
|
|
395
|
+
out = ProfileConfig.use_profile(out, base, base=base)
|
|
396
|
+
|
|
397
|
+
return out
|
|
398
|
+
|
|
200
399
|
@staticmethod
|
|
201
400
|
def use_profile(
|
|
202
401
|
conf: Diot,
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, Callable, List, Dict
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from diot import Diot
|
|
8
|
+
from panpath import PanPath
|
|
9
|
+
from ..caster import cast
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Loader(ABC):
|
|
13
|
+
|
|
14
|
+
CASTERS: List[Callable[[str, bool], Any]] | None = None
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _convert_path(conf: str | Path) -> Path:
|
|
18
|
+
"""Convert the conf to Path if it is a string"""
|
|
19
|
+
if isinstance(conf, (str, Path)):
|
|
20
|
+
return PanPath(conf)
|
|
21
|
+
return conf
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
25
|
+
"""Load the configuration from the path or configurations"""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
29
|
+
"""Asynchronously load the configuration from the path or configurations"""
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def _convert(cls, conf: Any, loaded: Any) -> Diot:
|
|
33
|
+
"""Convert the loaded configuration to Diot"""
|
|
34
|
+
if cls.CASTERS:
|
|
35
|
+
loaded = cast(loaded, cls.CASTERS)
|
|
36
|
+
|
|
37
|
+
return Diot(loaded)
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def _convert_with_profiles(cls, conf: Any, loaded: Any) -> Diot:
|
|
41
|
+
"""Convert the loaded configuration with profiles to Diot"""
|
|
42
|
+
return Diot(loaded)
|
|
43
|
+
|
|
44
|
+
def _exists(self, conf: str | Path, ignore_exist: bool) -> bool:
|
|
45
|
+
"""Check if the configuration file exists"""
|
|
46
|
+
path = self.__class__._convert_path(conf)
|
|
47
|
+
exists = path.exists()
|
|
48
|
+
if not ignore_exist and not exists:
|
|
49
|
+
raise FileNotFoundError(f"{conf} does not exist")
|
|
50
|
+
return exists
|
|
51
|
+
|
|
52
|
+
async def _a_exists(self, conf: str | Path, ignore_exist: bool) -> bool:
|
|
53
|
+
"""Asynchronously check if the configuration file exists"""
|
|
54
|
+
path = self.__class__._convert_path(conf)
|
|
55
|
+
exists = await path.a_exists()
|
|
56
|
+
if not ignore_exist and not exists:
|
|
57
|
+
raise FileNotFoundError(f"{conf} does not exist")
|
|
58
|
+
return exists
|
|
59
|
+
|
|
60
|
+
def load(self, conf: Any, ignore_nonexist: bool = False) -> Diot:
|
|
61
|
+
"""Load the configuration from the path or configurations and cast
|
|
62
|
+
values
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
conf: The configuration file to load
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The Diot object
|
|
69
|
+
"""
|
|
70
|
+
path = self.__class__._convert_path(conf)
|
|
71
|
+
loaded = self.loading(path, ignore_nonexist)
|
|
72
|
+
return self.__class__._convert(conf, loaded)
|
|
73
|
+
|
|
74
|
+
async def a_load(self, conf: Any, ignore_nonexist: bool = False) -> Diot:
|
|
75
|
+
"""Asynchronously load the configuration from the path or configurations
|
|
76
|
+
and cast values
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
conf: The configuration file to load
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The Diot object
|
|
83
|
+
"""
|
|
84
|
+
path = self.__class__._convert_path(conf)
|
|
85
|
+
loaded = await self.a_loading(path, ignore_nonexist)
|
|
86
|
+
return self.__class__._convert(conf, loaded)
|
|
87
|
+
|
|
88
|
+
def load_with_profiles( # type: ignore[override]
|
|
89
|
+
self,
|
|
90
|
+
conf: Any,
|
|
91
|
+
ignore_nonexist: bool = False,
|
|
92
|
+
) -> Diot:
|
|
93
|
+
"""Load the configuration from the path or configurations with profiles
|
|
94
|
+
and cast values
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
conf: The configuration file to load
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The Diot object
|
|
101
|
+
"""
|
|
102
|
+
path = self.__class__._convert_path(conf)
|
|
103
|
+
loaded = self.loading(path, ignore_nonexist)
|
|
104
|
+
return self.__class__._convert_with_profiles(conf, loaded)
|
|
105
|
+
|
|
106
|
+
async def a_load_with_profiles( # type: ignore[override]
|
|
107
|
+
self,
|
|
108
|
+
conf: Any,
|
|
109
|
+
ignore_nonexist: bool = False,
|
|
110
|
+
) -> Diot:
|
|
111
|
+
"""Asynchronously load the configuration from the path or configurations
|
|
112
|
+
with profiles and cast values
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
conf: The configuration file to load
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
The Diot object
|
|
119
|
+
"""
|
|
120
|
+
path = self.__class__._convert_path(conf)
|
|
121
|
+
loaded = await self.a_loading(path, ignore_nonexist)
|
|
122
|
+
return self.__class__._convert_with_profiles(conf, loaded)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class NoConvertingPathMixin(ABC):
|
|
126
|
+
"""String loader base class"""
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _convert_path(conf: str) -> str:
|
|
130
|
+
return conf
|
|
131
|
+
|
|
132
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
133
|
+
"""Asynchronously load the configuration from a toml file"""
|
|
134
|
+
return self.loading(conf, ignore_nonexist)
|
|
@@ -14,6 +14,10 @@ class DictLoader(Loader):
|
|
|
14
14
|
"""Load the configuration from a dict"""
|
|
15
15
|
return conf
|
|
16
16
|
|
|
17
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
18
|
+
"""Asynchronously load the configuration from a dict"""
|
|
19
|
+
return conf
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
class DictsLoader(NoConvertingPathMixin, DictLoader):
|
|
19
23
|
"""Dict string loader"""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
import io
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any, Dict
|
|
4
|
+
from typing import Any, Awaitable, Dict
|
|
5
5
|
from diot import Diot
|
|
6
6
|
|
|
7
7
|
from ..utils import require_package
|
|
@@ -46,25 +46,37 @@ class EnvLoader(Loader):
|
|
|
46
46
|
|
|
47
47
|
return dotenv.main.DotEnv(conf).dict()
|
|
48
48
|
|
|
49
|
-
def
|
|
50
|
-
|
|
49
|
+
async def a_loading(self, conf, ignore_nonexist):
|
|
50
|
+
"""Asynchronously load the configuration from a .env file"""
|
|
51
|
+
if hasattr(conf, "read"):
|
|
52
|
+
content = conf.read()
|
|
53
|
+
if isinstance(content, Awaitable):
|
|
54
|
+
content = await content
|
|
55
|
+
if isinstance(content, bytes):
|
|
56
|
+
content = content.decode()
|
|
57
|
+
return dotenv.dotenv_values(stream=io.StringIO(content))
|
|
58
|
+
|
|
59
|
+
if not await self._a_exists(conf, ignore_nonexist):
|
|
60
|
+
return {}
|
|
61
|
+
|
|
62
|
+
return dotenv.main.DotEnv(conf).dict()
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def _convert_with_profiles( # type: ignore[override]
|
|
66
|
+
cls,
|
|
51
67
|
conf: Any,
|
|
52
|
-
|
|
68
|
+
loaded: Dict[str, Any],
|
|
53
69
|
) -> Diot:
|
|
54
|
-
"""Load and cast the configuration from a .env file with profiles"""
|
|
55
|
-
envs = self.loading(conf, ignore_nonexist)
|
|
56
70
|
out = Diot()
|
|
57
|
-
for k, v in
|
|
71
|
+
for k, v in loaded.items():
|
|
58
72
|
if "_" not in k:
|
|
59
|
-
warnings.warn(
|
|
60
|
-
f"{Path(conf).name}: No profile name found in key: {k}"
|
|
61
|
-
)
|
|
73
|
+
warnings.warn(f"{Path(conf).name}: No profile name found in key: {k}")
|
|
62
74
|
continue
|
|
63
75
|
profile, key = k.split("_", 1)
|
|
64
76
|
profile = profile.lower()
|
|
65
77
|
out.setdefault(profile, Diot())[key] = v
|
|
66
78
|
|
|
67
|
-
return cast(out,
|
|
79
|
+
return cast(out, cls.CASTERS)
|
|
68
80
|
|
|
69
81
|
|
|
70
82
|
class EnvsLoader(NoConvertingPathMixin, EnvLoader):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import warnings
|
|
4
|
-
from typing import Any, Dict
|
|
4
|
+
from typing import Any, Awaitable, Dict
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from diot import Diot
|
|
7
7
|
|
|
@@ -47,10 +47,28 @@ class IniLoader(Loader):
|
|
|
47
47
|
|
|
48
48
|
return iniconfig.IniConfig(conf).sections
|
|
49
49
|
|
|
50
|
-
def
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
51
|
+
"""Asynchronously load the configuration from an ini-like file"""
|
|
52
|
+
if hasattr(conf, "read"):
|
|
53
|
+
content = conf.read()
|
|
54
|
+
if isinstance(content, Awaitable):
|
|
55
|
+
content = await content
|
|
56
|
+
if isinstance(content, bytes):
|
|
57
|
+
content = content.decode()
|
|
58
|
+
return iniconfig.IniConfig("<config>", content).sections
|
|
59
|
+
|
|
60
|
+
if not await self._a_exists(conf, ignore_nonexist):
|
|
61
|
+
return {"default": {}}
|
|
62
|
+
|
|
63
|
+
return iniconfig.IniConfig(conf).sections
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def _convert( # type: ignore[override]
|
|
67
|
+
cls,
|
|
68
|
+
conf: Any,
|
|
69
|
+
loaded: Dict[str, Any],
|
|
70
|
+
) -> Diot:
|
|
71
|
+
keys = list(loaded)
|
|
54
72
|
|
|
55
73
|
if hasattr(conf, "read"):
|
|
56
74
|
pathname = "<config>"
|
|
@@ -66,22 +84,19 @@ class IniLoader(Loader):
|
|
|
66
84
|
)
|
|
67
85
|
|
|
68
86
|
if len(keys) == 0 or keys[0].lower() != "default":
|
|
69
|
-
raise ValueError(
|
|
70
|
-
f"{pathname}: Only the default section can be loaded."
|
|
71
|
-
)
|
|
87
|
+
raise ValueError(f"{pathname}: Only the default section can be loaded.")
|
|
72
88
|
|
|
73
|
-
return cast(Diot(
|
|
89
|
+
return cast(Diot(loaded[keys[0]]), cls.CASTERS)
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
@classmethod
|
|
92
|
+
def _convert_with_profiles( # type: ignore[override]
|
|
93
|
+
cls,
|
|
77
94
|
conf: Any,
|
|
78
|
-
|
|
95
|
+
loaded: Dict[str, Any],
|
|
79
96
|
) -> Diot:
|
|
80
|
-
"""Load and cast the configuration from an ini-like file with profiles"""
|
|
81
|
-
sections = self.loading(conf, ignore_nonexist)
|
|
82
97
|
out = Diot()
|
|
83
|
-
for k, v in
|
|
84
|
-
out[k.lower()] = cast(v,
|
|
98
|
+
for k, v in loaded.items():
|
|
99
|
+
out[k.lower()] = cast(v, cls.CASTERS)
|
|
85
100
|
return out
|
|
86
101
|
|
|
87
102
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Awaitable, Dict
|
|
3
|
+
|
|
4
|
+
from . import Loader, NoConvertingPathMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JsonLoader(Loader):
|
|
8
|
+
"""Json file loader"""
|
|
9
|
+
|
|
10
|
+
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
11
|
+
"""Load the configuration from a json file"""
|
|
12
|
+
if hasattr(conf, "read"):
|
|
13
|
+
content = conf.read()
|
|
14
|
+
return json.loads(content)
|
|
15
|
+
|
|
16
|
+
if not self._exists(conf, ignore_nonexist):
|
|
17
|
+
return {}
|
|
18
|
+
|
|
19
|
+
with open(conf) as f:
|
|
20
|
+
return json.load(f)
|
|
21
|
+
|
|
22
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
23
|
+
"""Asynchronously load the configuration from a json file"""
|
|
24
|
+
if hasattr(conf, "read"):
|
|
25
|
+
content = conf.read()
|
|
26
|
+
if isinstance(content, Awaitable):
|
|
27
|
+
content = await content
|
|
28
|
+
if isinstance(content, bytes):
|
|
29
|
+
content = content.decode()
|
|
30
|
+
return json.loads(content)
|
|
31
|
+
|
|
32
|
+
if not await self._a_exists(conf, ignore_nonexist):
|
|
33
|
+
return {}
|
|
34
|
+
|
|
35
|
+
async with self.__class__._convert_path(conf).a_open() as f:
|
|
36
|
+
content = await f.read()
|
|
37
|
+
if isinstance(content, bytes): # pragma: no cover
|
|
38
|
+
content = content.decode()
|
|
39
|
+
return json.loads(content)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class JsonsLoader(NoConvertingPathMixin, JsonLoader):
|
|
43
|
+
"""Json string loader"""
|
|
44
|
+
|
|
45
|
+
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
46
|
+
"""Load the configuration from a json file"""
|
|
47
|
+
return json.loads(conf)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from os import environ
|
|
2
3
|
from typing import Any, Dict
|
|
3
|
-
import warnings
|
|
4
4
|
|
|
5
5
|
from diot import Diot
|
|
6
6
|
|
|
@@ -42,23 +42,27 @@ class OsenvLoader(NoConvertingPathMixin, Loader):
|
|
|
42
42
|
out[k[len_prefix:]] = v
|
|
43
43
|
return out
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
async def a_loading(
|
|
46
46
|
self,
|
|
47
47
|
conf: Any,
|
|
48
48
|
ignore_nonexist: bool = False,
|
|
49
|
+
) -> Dict[str, Any]:
|
|
50
|
+
"""Asynchronously load the configuration from environment variables"""
|
|
51
|
+
return self.loading(conf, ignore_nonexist)
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def _convert_with_profiles( # type: ignore[override]
|
|
55
|
+
cls,
|
|
56
|
+
conf: Any,
|
|
57
|
+
loaded: Dict[str, Any],
|
|
49
58
|
) -> Diot:
|
|
50
|
-
prefix = f"{conf[:-6]}_" if len(conf) > 6 else ""
|
|
51
|
-
len_prefix = len(prefix)
|
|
52
59
|
out = Diot()
|
|
53
|
-
for
|
|
54
|
-
if not k.startswith(prefix):
|
|
55
|
-
continue
|
|
56
|
-
key = k[len_prefix:]
|
|
60
|
+
for key, val in loaded.items():
|
|
57
61
|
if "_" not in key:
|
|
58
|
-
warnings.warn(f"{conf}: No profile name found in key: {
|
|
62
|
+
warnings.warn(f"{conf}: No profile name found in key: {key}")
|
|
59
63
|
continue
|
|
60
64
|
profile, key = key.split("_", 1)
|
|
61
65
|
profile = profile.lower()
|
|
62
|
-
out.setdefault(profile, Diot())[key] =
|
|
66
|
+
out.setdefault(profile, Diot())[key] = val
|
|
63
67
|
|
|
64
|
-
return cast(out,
|
|
68
|
+
return cast(out, cls.CASTERS)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
1
|
+
from typing import Any, Dict, Awaitable
|
|
2
2
|
|
|
3
3
|
from ..utils import require_package
|
|
4
4
|
from ..caster import (
|
|
@@ -34,6 +34,27 @@ class TomlLoader(Loader):
|
|
|
34
34
|
with open(conf, "r") as f: # rtoml
|
|
35
35
|
return toml.load(f)
|
|
36
36
|
|
|
37
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
38
|
+
"""Asynchronously load the configuration from a toml file"""
|
|
39
|
+
if hasattr(conf, "read"):
|
|
40
|
+
content = conf.read()
|
|
41
|
+
if isinstance(content, Awaitable):
|
|
42
|
+
content = await content
|
|
43
|
+
if isinstance(content, bytes):
|
|
44
|
+
content = content.decode()
|
|
45
|
+
return toml.loads(content)
|
|
46
|
+
|
|
47
|
+
if not await self._a_exists(conf, ignore_nonexist):
|
|
48
|
+
return {}
|
|
49
|
+
|
|
50
|
+
async with self.__class__._convert_path(conf).a_open("rb") as f:
|
|
51
|
+
content = await f.read()
|
|
52
|
+
try:
|
|
53
|
+
return toml.loads(content)
|
|
54
|
+
except TypeError:
|
|
55
|
+
content = content.decode()
|
|
56
|
+
return toml.loads(content)
|
|
57
|
+
|
|
37
58
|
|
|
38
59
|
class TomlsLoader(NoConvertingPathMixin, TomlLoader):
|
|
39
60
|
"""Toml string loader"""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
1
|
+
from typing import Any, Dict, Awaitable
|
|
2
2
|
|
|
3
3
|
from . import Loader, NoConvertingPathMixin
|
|
4
4
|
from ..utils import require_package
|
|
@@ -21,6 +21,25 @@ class YamlLoader(Loader):
|
|
|
21
21
|
with open(conf) as f:
|
|
22
22
|
return yaml.load(f, Loader=yaml.FullLoader)
|
|
23
23
|
|
|
24
|
+
async def a_loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
25
|
+
"""Asynchronously load the configuration from a yaml file"""
|
|
26
|
+
if hasattr(conf, "read"):
|
|
27
|
+
content = conf.read()
|
|
28
|
+
if isinstance(content, Awaitable):
|
|
29
|
+
content = await content
|
|
30
|
+
if isinstance(content, bytes):
|
|
31
|
+
content = content.decode()
|
|
32
|
+
return yaml.load(content, Loader=yaml.FullLoader)
|
|
33
|
+
|
|
34
|
+
if not await self._a_exists(conf, ignore_nonexist):
|
|
35
|
+
return {}
|
|
36
|
+
|
|
37
|
+
async with self.__class__._convert_path(conf).a_open() as f:
|
|
38
|
+
content = await f.read()
|
|
39
|
+
if isinstance(content, bytes): # pragma: no cover
|
|
40
|
+
content = content.decode()
|
|
41
|
+
return yaml.load(content, Loader=yaml.FullLoader)
|
|
42
|
+
|
|
24
43
|
|
|
25
44
|
class YamlsLoader(NoConvertingPathMixin, YamlLoader):
|
|
26
45
|
"""Yaml string loader"""
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import Any, Callable, List, Dict
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from diot import Diot
|
|
8
|
-
from ..caster import cast
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Loader(ABC):
|
|
12
|
-
|
|
13
|
-
CASTERS: List[Callable[[str, bool], Any]] | None = None
|
|
14
|
-
|
|
15
|
-
@staticmethod
|
|
16
|
-
def _convert_path(conf: str | Path) -> Path:
|
|
17
|
-
"""Convert the conf to Path if it is a string"""
|
|
18
|
-
try:
|
|
19
|
-
from yunpath import AnyPath
|
|
20
|
-
except ImportError:
|
|
21
|
-
AnyPath = Path
|
|
22
|
-
|
|
23
|
-
if isinstance(conf, str):
|
|
24
|
-
return AnyPath(conf)
|
|
25
|
-
return conf
|
|
26
|
-
|
|
27
|
-
@abstractmethod
|
|
28
|
-
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
29
|
-
"""Load the configuration from the path or configurations"""
|
|
30
|
-
|
|
31
|
-
def _exists(self, conf: str | Path, ignore_exist: bool) -> bool:
|
|
32
|
-
"""Check if the configuration file exists"""
|
|
33
|
-
path = self.__class__._convert_path(conf)
|
|
34
|
-
if not ignore_exist and not path.exists():
|
|
35
|
-
raise FileNotFoundError(f"{conf} does not exist")
|
|
36
|
-
return path.exists()
|
|
37
|
-
|
|
38
|
-
def load(self, conf: Any, ignore_nonexist: bool = False) -> Diot:
|
|
39
|
-
"""Load the configuration from the path or configurations and cast
|
|
40
|
-
values
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
conf: The configuration file to load
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
The Diot object
|
|
47
|
-
"""
|
|
48
|
-
path = self.__class__._convert_path(conf)
|
|
49
|
-
loaded = self.loading(path, ignore_nonexist)
|
|
50
|
-
if self.__class__.CASTERS:
|
|
51
|
-
loaded = cast(loaded, self.__class__.CASTERS)
|
|
52
|
-
|
|
53
|
-
return Diot(loaded)
|
|
54
|
-
|
|
55
|
-
load_with_profiles = load
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class NoConvertingPathMixin(ABC):
|
|
59
|
-
"""String loader base class"""
|
|
60
|
-
|
|
61
|
-
@staticmethod
|
|
62
|
-
def _convert_path(conf: str) -> str:
|
|
63
|
-
return conf
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import Any, Dict
|
|
3
|
-
|
|
4
|
-
from . import Loader, NoConvertingPathMixin
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class JsonLoader(Loader):
|
|
8
|
-
"""Json file loader"""
|
|
9
|
-
|
|
10
|
-
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
11
|
-
"""Load the configuration from a json file"""
|
|
12
|
-
if hasattr(conf, "read"):
|
|
13
|
-
content = conf.read()
|
|
14
|
-
return json.loads(content)
|
|
15
|
-
|
|
16
|
-
if not self._exists(conf, ignore_nonexist):
|
|
17
|
-
return {}
|
|
18
|
-
|
|
19
|
-
with open(conf) as f:
|
|
20
|
-
return json.load(f)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class JsonsLoader(NoConvertingPathMixin, JsonLoader):
|
|
24
|
-
"""Json string loader"""
|
|
25
|
-
|
|
26
|
-
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
27
|
-
"""Load the configuration from a json file"""
|
|
28
|
-
return json.loads(conf)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|