python-simpleconf 0.7.0__tar.gz → 0.7.2__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.0 → python_simpleconf-0.7.2}/PKG-INFO +5 -3
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/pyproject.toml +7 -5
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/__init__.py +1 -1
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/config.py +38 -8
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/__init__.py +25 -5
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/dict.py +6 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/env.py +2 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/ini.py +2 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/json.py +2 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/osenv.py +2 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/toml.py +2 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/loaders/yaml.py +2 -2
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/LICENSE +0 -0
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/README.md +0 -0
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/caster.py +0 -0
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/exceptions.py +0 -0
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/py.typed +0 -0
- {python_simpleconf-0.7.0 → python_simpleconf-0.7.2}/simpleconf/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-simpleconf
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Simple configuration management with python.
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: pwwang
|
|
@@ -14,16 +14,18 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Provides-Extra: all
|
|
17
|
+
Provides-Extra: cloud
|
|
17
18
|
Provides-Extra: env
|
|
18
19
|
Provides-Extra: ini
|
|
19
20
|
Provides-Extra: toml
|
|
20
21
|
Provides-Extra: yaml
|
|
21
|
-
Requires-Dist: diot (>=0.3.
|
|
22
|
+
Requires-Dist: diot (>=0.3.2,<0.4.0)
|
|
22
23
|
Requires-Dist: iniconfig (>=2.0,<3.0) ; extra == "ini" or extra == "all"
|
|
23
|
-
Requires-Dist: python-dotenv (>=1.
|
|
24
|
+
Requires-Dist: python-dotenv (>=1.1,<2.0) ; extra == "env" or extra == "all"
|
|
24
25
|
Requires-Dist: pyyaml (>=6,<7) ; extra == "yaml" or extra == "all"
|
|
25
26
|
Requires-Dist: rtoml (>=0.12,<0.13) ; (sys_platform == "linux") and (extra == "toml" or extra == "all")
|
|
26
27
|
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"
|
|
27
29
|
Project-URL: Homepage, https://github.com/pwwang/simpleconf
|
|
28
30
|
Project-URL: Repository, https://github.com/pwwang/simpleconf
|
|
29
31
|
Description-Content-Type: text/markdown
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = [ "poetry>=
|
|
2
|
+
requires = [ "poetry>=2",]
|
|
3
3
|
build-backend = "poetry.masonry.api"
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "python-simpleconf"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.2"
|
|
8
8
|
description = "Simple configuration management with python."
|
|
9
9
|
authors = [ "pwwang <pwwang@pwwang.com>",]
|
|
10
10
|
license = "MIT"
|
|
@@ -17,13 +17,14 @@ packages = [
|
|
|
17
17
|
|
|
18
18
|
[tool.poetry.dependencies]
|
|
19
19
|
python = "^3.9"
|
|
20
|
-
diot = "^0.3.
|
|
21
|
-
python-dotenv = { version="^1.
|
|
20
|
+
diot = "^0.3.2"
|
|
21
|
+
python-dotenv = { version="^1.1", optional = true}
|
|
22
22
|
pyyaml = { version="^6", optional = true}
|
|
23
23
|
# Use rtoml only when the wheel is available (linux)
|
|
24
24
|
rtoml = {version = "^0.12", optional = true, platform = "linux"}
|
|
25
25
|
tomli = {version = "^2.0", optional = true, markers = 'sys_platform != "linux"'}
|
|
26
26
|
iniconfig = {version = "^2.0", optional = true}
|
|
27
|
+
yunpath = {version = "^0.0.4", optional = true}
|
|
27
28
|
|
|
28
29
|
[tool.poetry.extras]
|
|
29
30
|
ini = [ "iniconfig" ]
|
|
@@ -31,8 +32,9 @@ env = [ "python-dotenv"]
|
|
|
31
32
|
yaml = [ "pyyaml"]
|
|
32
33
|
toml = [ "rtoml", "tomli"]
|
|
33
34
|
all = [ "iniconfig", "python-dotenv", "pyyaml", "rtoml", "tomli"]
|
|
35
|
+
cloud = [ "yunpath"]
|
|
34
36
|
|
|
35
|
-
[tool.poetry.dev
|
|
37
|
+
[tool.poetry.group.dev.dependencies]
|
|
36
38
|
pytest = "^8"
|
|
37
39
|
pytest-cov = "^6"
|
|
38
40
|
|
|
@@ -87,6 +87,8 @@ class ProfileConfig:
|
|
|
87
87
|
*configs: Any,
|
|
88
88
|
loader: LoaderType | Sequence[LoaderType] = None,
|
|
89
89
|
ignore_nonexist: bool = False,
|
|
90
|
+
base: str = "default",
|
|
91
|
+
allow_missing_base: bool = False,
|
|
90
92
|
) -> Diot:
|
|
91
93
|
"""Load the configuration from the files, or other configurations
|
|
92
94
|
|
|
@@ -98,6 +100,10 @@ class ProfileConfig:
|
|
|
98
100
|
same length as configs.
|
|
99
101
|
ignore_nonexist: Whether to ignore non-existent files
|
|
100
102
|
Otherwise, will raise errors
|
|
103
|
+
base: The default profile to use after loading
|
|
104
|
+
allow_missing_base: Whether to allow missing base profile
|
|
105
|
+
If False, will raise errors when the base profile is not found
|
|
106
|
+
in the loaded profiles.
|
|
101
107
|
"""
|
|
102
108
|
if not isinstance(loader, Sequence) or isinstance(loader, str):
|
|
103
109
|
loader = [loader] * len(configs)
|
|
@@ -132,7 +138,12 @@ class ProfileConfig:
|
|
|
132
138
|
pool.setdefault(profile, Diot())
|
|
133
139
|
pool[profile].update_recursively(value)
|
|
134
140
|
|
|
135
|
-
|
|
141
|
+
if base and base not in pool and not allow_missing_base:
|
|
142
|
+
raise ValueError(f"Base profile '{base}' not found")
|
|
143
|
+
|
|
144
|
+
if base and base in pool:
|
|
145
|
+
out = ProfileConfig.use_profile(out, base, base=base)
|
|
146
|
+
|
|
136
147
|
return out
|
|
137
148
|
|
|
138
149
|
@staticmethod
|
|
@@ -140,6 +151,8 @@ class ProfileConfig:
|
|
|
140
151
|
conf: Any,
|
|
141
152
|
loader: str | Loader | None = None,
|
|
142
153
|
ignore_nonexist: bool = False,
|
|
154
|
+
base: str = "default",
|
|
155
|
+
allow_missing_base: bool = False,
|
|
143
156
|
) -> Diot:
|
|
144
157
|
"""Load the configuration from the file
|
|
145
158
|
|
|
@@ -148,6 +161,7 @@ class ProfileConfig:
|
|
|
148
161
|
loader: The loader to use. Will detect from conf by default
|
|
149
162
|
ignore_nonexist: Whether to ignore non-existent files
|
|
150
163
|
Otherwise, will raise errors
|
|
164
|
+
base: The default profile to use after loading
|
|
151
165
|
|
|
152
166
|
Returns:
|
|
153
167
|
A Diot object with the loaded configuration
|
|
@@ -175,7 +189,12 @@ class ProfileConfig:
|
|
|
175
189
|
pool.setdefault(profile, Diot())
|
|
176
190
|
pool[profile].update_recursively(value)
|
|
177
191
|
|
|
178
|
-
|
|
192
|
+
if base and base not in pool and not allow_missing_base:
|
|
193
|
+
raise ValueError(f"Base profile '{base}' not found")
|
|
194
|
+
|
|
195
|
+
if base and base in pool:
|
|
196
|
+
out = ProfileConfig.use_profile(out, base, base=base)
|
|
197
|
+
|
|
179
198
|
return out
|
|
180
199
|
|
|
181
200
|
@staticmethod
|
|
@@ -184,24 +203,35 @@ class ProfileConfig:
|
|
|
184
203
|
profile: str,
|
|
185
204
|
base: str = "default",
|
|
186
205
|
copy: bool = False,
|
|
206
|
+
allow_missing_base: bool = False,
|
|
187
207
|
) -> Diot:
|
|
188
208
|
"""Switch the configuration to the given profile, based on the
|
|
189
|
-
|
|
209
|
+
base profile.
|
|
190
210
|
|
|
191
211
|
Args:
|
|
192
212
|
conf: The configuration object by the `load` function
|
|
193
213
|
profile: The profile to use
|
|
194
|
-
|
|
214
|
+
base: The base profile.
|
|
215
|
+
If it does not exist, no base profile will be used.
|
|
216
|
+
copy: Whether to return a new configuration object
|
|
217
|
+
If False, the given configuration object will be updated
|
|
218
|
+
in-place.
|
|
219
|
+
allow_missing_base: Whether to allow missing base profile
|
|
220
|
+
If False, will raise errors when the base profile is not found
|
|
221
|
+
in the loaded profiles.
|
|
195
222
|
|
|
196
223
|
Returns:
|
|
197
224
|
The configuration object with the switched profile if copy is True
|
|
198
225
|
Otherwise None (updated in-place)
|
|
199
226
|
"""
|
|
200
227
|
pool = conf[POOL_KEY]
|
|
228
|
+
if base and base not in pool and not allow_missing_base:
|
|
229
|
+
raise ValueError(f"Base profile '{base}' not found")
|
|
230
|
+
|
|
201
231
|
if copy:
|
|
202
232
|
out = Diot({POOL_KEY: pool, META_KEY: conf[META_KEY].copy()})
|
|
203
|
-
if base is not None:
|
|
204
|
-
out.update_recursively(pool
|
|
233
|
+
if base is not None and base != profile:
|
|
234
|
+
out.update_recursively(pool.get(base, {}))
|
|
205
235
|
out[META_KEY]["current_profile"] = profile
|
|
206
236
|
out[META_KEY]["base_profile"] = base
|
|
207
237
|
out.update_recursively(pool[profile])
|
|
@@ -213,8 +243,8 @@ class ProfileConfig:
|
|
|
213
243
|
continue
|
|
214
244
|
del conf[key]
|
|
215
245
|
|
|
216
|
-
if base is not None:
|
|
217
|
-
conf.update_recursively(pool
|
|
246
|
+
if base is not None and base != profile:
|
|
247
|
+
conf.update_recursively(pool.get(base, {}))
|
|
218
248
|
conf.update_recursively(pool[profile])
|
|
219
249
|
conf[META_KEY]["current_profile"] = profile
|
|
220
250
|
conf[META_KEY]["base_profile"] = base
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from os import PathLike
|
|
5
|
-
from pathlib import Path
|
|
6
4
|
from typing import Any, Callable, List, Dict
|
|
5
|
+
from pathlib import Path
|
|
7
6
|
|
|
8
7
|
from diot import Diot
|
|
9
8
|
from ..caster import cast
|
|
@@ -13,13 +12,25 @@ class Loader(ABC):
|
|
|
13
12
|
|
|
14
13
|
CASTERS: List[Callable[[str, bool], Any]] | None = None
|
|
15
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
|
+
|
|
16
27
|
@abstractmethod
|
|
17
28
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
18
29
|
"""Load the configuration from the path or configurations"""
|
|
19
30
|
|
|
20
|
-
def _exists(self, conf:
|
|
31
|
+
def _exists(self, conf: str | Path, ignore_exist: bool) -> bool:
|
|
21
32
|
"""Check if the configuration file exists"""
|
|
22
|
-
path =
|
|
33
|
+
path = self.__class__._convert_path(conf)
|
|
23
34
|
if not ignore_exist and not path.exists():
|
|
24
35
|
raise FileNotFoundError(f"{conf} does not exist")
|
|
25
36
|
return path.exists()
|
|
@@ -34,10 +45,19 @@ class Loader(ABC):
|
|
|
34
45
|
Returns:
|
|
35
46
|
The Diot object
|
|
36
47
|
"""
|
|
37
|
-
|
|
48
|
+
path = self.__class__._convert_path(conf)
|
|
49
|
+
loaded = self.loading(path, ignore_nonexist)
|
|
38
50
|
if self.__class__.CASTERS:
|
|
39
51
|
loaded = cast(loaded, self.__class__.CASTERS)
|
|
40
52
|
|
|
41
53
|
return Diot(loaded)
|
|
42
54
|
|
|
43
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,17 +1,21 @@
|
|
|
1
1
|
from typing import Any, Dict
|
|
2
2
|
|
|
3
|
-
from . import Loader
|
|
3
|
+
from . import Loader, NoConvertingPathMixin
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class DictLoader(Loader):
|
|
7
7
|
"""Dict loader"""
|
|
8
8
|
|
|
9
|
+
@staticmethod
|
|
10
|
+
def _convert_path(conf):
|
|
11
|
+
return conf
|
|
12
|
+
|
|
9
13
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
10
14
|
"""Load the configuration from a dict"""
|
|
11
15
|
return conf
|
|
12
16
|
|
|
13
17
|
|
|
14
|
-
class DictsLoader(DictLoader):
|
|
18
|
+
class DictsLoader(NoConvertingPathMixin, DictLoader):
|
|
15
19
|
"""Dict string loader"""
|
|
16
20
|
|
|
17
21
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
@@ -16,7 +16,7 @@ from ..caster import (
|
|
|
16
16
|
json_caster,
|
|
17
17
|
toml_caster,
|
|
18
18
|
)
|
|
19
|
-
from . import Loader
|
|
19
|
+
from . import Loader, NoConvertingPathMixin
|
|
20
20
|
|
|
21
21
|
dotenv = require_package("dotenv")
|
|
22
22
|
|
|
@@ -67,7 +67,7 @@ class EnvLoader(Loader):
|
|
|
67
67
|
return cast(out, self.__class__.CASTERS)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
class EnvsLoader(EnvLoader):
|
|
70
|
+
class EnvsLoader(NoConvertingPathMixin, EnvLoader):
|
|
71
71
|
"""Env string loader"""
|
|
72
72
|
|
|
73
73
|
def loading(self, conf: Any, ignore_nonexist: bool = False) -> Dict[str, Any]:
|
|
@@ -17,7 +17,7 @@ from ..caster import (
|
|
|
17
17
|
json_caster,
|
|
18
18
|
toml_caster,
|
|
19
19
|
)
|
|
20
|
-
from . import Loader
|
|
20
|
+
from . import Loader, NoConvertingPathMixin
|
|
21
21
|
|
|
22
22
|
iniconfig = require_package("iniconfig")
|
|
23
23
|
|
|
@@ -85,7 +85,7 @@ class IniLoader(Loader):
|
|
|
85
85
|
return out
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
class InisLoader(IniLoader):
|
|
88
|
+
class InisLoader(NoConvertingPathMixin, IniLoader):
|
|
89
89
|
"""Ini-like string loader"""
|
|
90
90
|
|
|
91
91
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from typing import Any, Dict
|
|
3
3
|
|
|
4
|
-
from . import Loader
|
|
4
|
+
from . import Loader, NoConvertingPathMixin
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class JsonLoader(Loader):
|
|
@@ -20,7 +20,7 @@ class JsonLoader(Loader):
|
|
|
20
20
|
return json.load(f)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class JsonsLoader(JsonLoader):
|
|
23
|
+
class JsonsLoader(NoConvertingPathMixin, JsonLoader):
|
|
24
24
|
"""Json string loader"""
|
|
25
25
|
|
|
26
26
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
@@ -4,7 +4,7 @@ import warnings
|
|
|
4
4
|
|
|
5
5
|
from diot import Diot
|
|
6
6
|
|
|
7
|
-
from . import Loader
|
|
7
|
+
from . import Loader, NoConvertingPathMixin
|
|
8
8
|
from ..caster import (
|
|
9
9
|
cast,
|
|
10
10
|
int_caster,
|
|
@@ -18,7 +18,7 @@ from ..caster import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class OsenvLoader(Loader):
|
|
21
|
+
class OsenvLoader(NoConvertingPathMixin, Loader):
|
|
22
22
|
"""Environment variable loader"""
|
|
23
23
|
|
|
24
24
|
CASTERS = [
|
|
@@ -5,7 +5,7 @@ from ..caster import (
|
|
|
5
5
|
none_caster,
|
|
6
6
|
null_caster,
|
|
7
7
|
)
|
|
8
|
-
from . import Loader
|
|
8
|
+
from . import Loader, NoConvertingPathMixin
|
|
9
9
|
|
|
10
10
|
toml = require_package("rtoml", "tomllib", "tomli")
|
|
11
11
|
|
|
@@ -35,7 +35,7 @@ class TomlLoader(Loader):
|
|
|
35
35
|
return toml.load(f)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class TomlsLoader(TomlLoader):
|
|
38
|
+
class TomlsLoader(NoConvertingPathMixin, TomlLoader):
|
|
39
39
|
"""Toml string loader"""
|
|
40
40
|
|
|
41
41
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Any, Dict
|
|
2
2
|
|
|
3
|
-
from . import Loader
|
|
3
|
+
from . import Loader, NoConvertingPathMixin
|
|
4
4
|
from ..utils import require_package
|
|
5
5
|
|
|
6
6
|
yaml = require_package("yaml")
|
|
@@ -22,7 +22,7 @@ class YamlLoader(Loader):
|
|
|
22
22
|
return yaml.load(f, Loader=yaml.FullLoader)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class YamlsLoader(YamlLoader):
|
|
25
|
+
class YamlsLoader(NoConvertingPathMixin, YamlLoader):
|
|
26
26
|
"""Yaml string loader"""
|
|
27
27
|
|
|
28
28
|
def loading(self, conf: Any, ignore_nonexist: bool) -> Dict[str, Any]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|