python-simpleconf 0.5.9__tar.gz → 0.6.1__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.5.9 → python_simpleconf-0.6.1}/PKG-INFO +10 -9
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/pyproject.toml +12 -10
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/__init__.py +1 -1
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/caster.py +9 -6
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/config.py +19 -4
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/exceptions.py +0 -1
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/__init__.py +4 -2
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/env.py +1 -1
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/ini.py +6 -5
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/osenv.py +2 -2
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/toml.py +7 -3
- python_simpleconf-0.6.1/simpleconf/py.typed +0 -0
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/utils.py +28 -7
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/LICENSE +0 -0
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/README.md +0 -0
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/dict.py +0 -0
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/json.py +0 -0
- {python_simpleconf-0.5.9 → python_simpleconf-0.6.1}/simpleconf/loaders/yaml.py +0 -0
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: python-simpleconf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Simple configuration management with python.
|
|
5
|
-
Home-page: https://github.com/pwwang/simpleconf
|
|
6
5
|
License: MIT
|
|
7
6
|
Author: pwwang
|
|
8
7
|
Author-email: pwwang@pwwang.com
|
|
9
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
10
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
10
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
14
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Provides-Extra: all
|
|
18
17
|
Provides-Extra: env
|
|
19
18
|
Provides-Extra: ini
|
|
20
19
|
Provides-Extra: toml
|
|
21
20
|
Provides-Extra: yaml
|
|
22
|
-
Requires-Dist: diot (>=0.
|
|
21
|
+
Requires-Dist: diot (>=0.3.1,<0.4.0)
|
|
23
22
|
Requires-Dist: iniconfig (>=2.0,<3.0) ; extra == "ini" or extra == "all"
|
|
24
|
-
Requires-Dist: python-dotenv (>=0.
|
|
23
|
+
Requires-Dist: python-dotenv (>=1.0,<2.0) ; extra == "env" or extra == "all"
|
|
25
24
|
Requires-Dist: pyyaml (>=6,<7) ; extra == "yaml" or extra == "all"
|
|
26
|
-
Requires-Dist: rtoml (>=0.
|
|
25
|
+
Requires-Dist: rtoml (>=0.12,<0.13) ; (sys_platform == "linux") and (extra == "toml" or extra == "all")
|
|
26
|
+
Requires-Dist: tomli (>=2.0,<3.0) ; (sys_platform != "linux") and (extra == "toml" or extra == "all")
|
|
27
|
+
Project-URL: Homepage, https://github.com/pwwang/simpleconf
|
|
27
28
|
Project-URL: Repository, https://github.com/pwwang/simpleconf
|
|
28
29
|
Description-Content-Type: text/markdown
|
|
29
30
|
|
|
@@ -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.6.1"
|
|
8
8
|
description = "Simple configuration management with python."
|
|
9
9
|
authors = [ "pwwang <pwwang@pwwang.com>",]
|
|
10
10
|
license = "MIT"
|
|
@@ -16,23 +16,25 @@ packages = [
|
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[tool.poetry.dependencies]
|
|
19
|
-
python = "^3.
|
|
20
|
-
diot = "^0.
|
|
21
|
-
python-dotenv = { version="^0
|
|
19
|
+
python = "^3.9"
|
|
20
|
+
diot = "^0.3.1"
|
|
21
|
+
python-dotenv = { version="^1.0", optional = true}
|
|
22
22
|
pyyaml = { version="^6", optional = true}
|
|
23
|
-
rtoml
|
|
23
|
+
# Use rtoml only when the wheel is available (linux)
|
|
24
|
+
rtoml = {version = "^0.12", optional = true, platform = "linux"}
|
|
25
|
+
tomli = {version = "^2.0", optional = true, markers = 'sys_platform != "linux"'}
|
|
24
26
|
iniconfig = {version = "^2.0", optional = true}
|
|
25
27
|
|
|
26
28
|
[tool.poetry.extras]
|
|
27
29
|
ini = [ "iniconfig" ]
|
|
28
30
|
env = [ "python-dotenv"]
|
|
29
31
|
yaml = [ "pyyaml"]
|
|
30
|
-
toml = [ "rtoml"]
|
|
31
|
-
all = [ "iniconfig", "python-dotenv", "pyyaml", "rtoml"]
|
|
32
|
+
toml = [ "rtoml", "tomli"]
|
|
33
|
+
all = [ "iniconfig", "python-dotenv", "pyyaml", "rtoml", "tomli"]
|
|
32
34
|
|
|
33
35
|
[tool.poetry.dev-dependencies]
|
|
34
|
-
pytest = "^
|
|
35
|
-
pytest-cov = "^
|
|
36
|
+
pytest = "^8"
|
|
37
|
+
pytest-cov = "^6"
|
|
36
38
|
|
|
37
39
|
[tool.pytest.ini_options]
|
|
38
40
|
addopts = "-vv -p no:asyncio --cov=simpleconf --cov-report xml:cov.xml --cov-report term-missing"
|
|
@@ -43,6 +45,6 @@ console_output_style = "progress"
|
|
|
43
45
|
junit_family = "xunit1"
|
|
44
46
|
|
|
45
47
|
[tool.black]
|
|
46
|
-
line-length =
|
|
48
|
+
line-length = 88
|
|
47
49
|
target-version = ['py37', 'py38', 'py39', 'py310']
|
|
48
50
|
include = '\.pyi?$'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import json
|
|
2
|
-
from typing import
|
|
4
|
+
from typing import Any, Callable, Sequence, TYPE_CHECKING
|
|
3
5
|
from ast import literal_eval
|
|
4
6
|
|
|
5
|
-
from simpleconf.utils import require_package
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
8
9
|
from diot import Diot
|
|
@@ -47,8 +48,10 @@ def _cast_bool(value: str) -> bool:
|
|
|
47
48
|
|
|
48
49
|
def _cast_toml(value: str) -> Any:
|
|
49
50
|
"""Cast toml string"""
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
from .utils import require_package
|
|
52
|
+
|
|
53
|
+
toml = require_package("rtoml", "tomllib", "tomli")
|
|
54
|
+
return toml.loads(value)
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
int_caster = type_caster("@int:", lambda x: int(float(x)))
|
|
@@ -74,11 +77,11 @@ def cast_value(value: Any, casters: Sequence[Callable]) -> Any:
|
|
|
74
77
|
return value
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
def cast(conf:
|
|
80
|
+
def cast(conf: Diot, casters: Sequence[Callable]) -> Diot:
|
|
78
81
|
"""Cast the configuration"""
|
|
79
82
|
for key, value in conf.items():
|
|
80
83
|
if isinstance(value, dict):
|
|
81
|
-
conf[key] = cast(value, casters)
|
|
84
|
+
conf[key] = cast(value, casters) # type: ignore[arg-type]
|
|
82
85
|
else:
|
|
83
86
|
conf[key] = cast_value(value, casters)
|
|
84
87
|
return conf
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from contextlib import contextmanager
|
|
2
|
-
from typing import Any, List
|
|
4
|
+
from typing import TYPE_CHECKING, Any, List, Generator
|
|
3
5
|
|
|
4
6
|
from diot import Diot
|
|
5
7
|
|
|
6
8
|
from .utils import config_to_ext, get_loader, POOL_KEY, META_KEY
|
|
7
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .loaders import Loader
|
|
12
|
+
|
|
8
13
|
|
|
9
14
|
class Config:
|
|
10
15
|
"""The configuration class"""
|
|
@@ -34,7 +39,9 @@ class Config:
|
|
|
34
39
|
|
|
35
40
|
@staticmethod
|
|
36
41
|
def load_one(
|
|
37
|
-
config,
|
|
42
|
+
config,
|
|
43
|
+
loader: str | Loader | None = None,
|
|
44
|
+
ignore_nonexist: bool = False,
|
|
38
45
|
) -> Diot:
|
|
39
46
|
"""Load the configuration from the file
|
|
40
47
|
|
|
@@ -90,7 +97,9 @@ class ProfileConfig:
|
|
|
90
97
|
|
|
91
98
|
@staticmethod
|
|
92
99
|
def load_one(
|
|
93
|
-
conf: Any,
|
|
100
|
+
conf: Any,
|
|
101
|
+
loader: str | Loader | None = None,
|
|
102
|
+
ignore_nonexist: bool = False,
|
|
94
103
|
) -> Diot:
|
|
95
104
|
"""Load the configuration from the file
|
|
96
105
|
|
|
@@ -167,6 +176,8 @@ class ProfileConfig:
|
|
|
167
176
|
conf[META_KEY]["current_profile"] = profile
|
|
168
177
|
conf[META_KEY]["base_profile"] = base
|
|
169
178
|
|
|
179
|
+
return conf
|
|
180
|
+
|
|
170
181
|
@staticmethod
|
|
171
182
|
def current_profile(conf: Diot) -> str:
|
|
172
183
|
"""Get the current profile"""
|
|
@@ -228,7 +239,11 @@ class ProfileConfig:
|
|
|
228
239
|
|
|
229
240
|
@staticmethod
|
|
230
241
|
@contextmanager
|
|
231
|
-
def with_profile(
|
|
242
|
+
def with_profile(
|
|
243
|
+
conf: Diot,
|
|
244
|
+
profile: str,
|
|
245
|
+
base: str = "default",
|
|
246
|
+
) -> Generator[Diot, None, None]:
|
|
232
247
|
"""A context manager to use the given profile
|
|
233
248
|
|
|
234
249
|
Args:
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import ABC, abstractmethod
|
|
2
4
|
from os import PathLike
|
|
3
5
|
from pathlib import Path
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, List
|
|
5
7
|
|
|
6
8
|
from ..caster import cast
|
|
7
9
|
|
|
@@ -11,7 +13,7 @@ if TYPE_CHECKING:
|
|
|
11
13
|
|
|
12
14
|
class Loader(ABC):
|
|
13
15
|
|
|
14
|
-
CASTERS = None
|
|
16
|
+
CASTERS: List[Callable[[str, bool], Any]] | None = None
|
|
15
17
|
|
|
16
18
|
@abstractmethod
|
|
17
19
|
def loading(self, conf: Any, ignore_nonexist: bool) -> "Diot":
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import warnings
|
|
2
4
|
from typing import Any
|
|
3
5
|
from pathlib import Path
|
|
@@ -34,13 +36,13 @@ class IniLoader(Loader):
|
|
|
34
36
|
toml_caster,
|
|
35
37
|
]
|
|
36
38
|
|
|
37
|
-
def loading(self, conf: Any, ignore_nonexist: bool) ->
|
|
39
|
+
def loading(self, conf: Any, ignore_nonexist: bool) -> Diot:
|
|
38
40
|
"""Load the configuration from an ini-like file"""
|
|
39
41
|
if not self._exists(conf, ignore_nonexist):
|
|
40
42
|
return Diot(default={})
|
|
41
43
|
return iniconfig.IniConfig(conf).sections
|
|
42
44
|
|
|
43
|
-
def load(self, conf: Any, ignore_nonexist: bool = False) ->
|
|
45
|
+
def load(self, conf: Any, ignore_nonexist: bool = False) -> Diot:
|
|
44
46
|
"""Load and cast the configuration from an ini-like file"""
|
|
45
47
|
sections = self.loading(conf, ignore_nonexist)
|
|
46
48
|
keys = list(sections)
|
|
@@ -58,13 +60,12 @@ class IniLoader(Loader):
|
|
|
58
60
|
|
|
59
61
|
return cast(Diot(sections[keys[0]]), self.__class__.CASTERS)
|
|
60
62
|
|
|
61
|
-
def load_with_profiles(
|
|
63
|
+
def load_with_profiles( # type: ignore[override]
|
|
62
64
|
self,
|
|
63
65
|
conf: Any,
|
|
64
66
|
ignore_nonexist: bool = False,
|
|
65
67
|
) -> Diot:
|
|
66
|
-
"""Load and cast the configuration from an ini-like file with profiles
|
|
67
|
-
"""
|
|
68
|
+
"""Load and cast the configuration from an ini-like file with profiles"""
|
|
68
69
|
sections = self.loading(conf, ignore_nonexist)
|
|
69
70
|
out = Diot()
|
|
70
71
|
for k, v in sections.items():
|
|
@@ -39,10 +39,10 @@ class OsenvLoader(Loader):
|
|
|
39
39
|
out = Diot()
|
|
40
40
|
for k, v in environ.items():
|
|
41
41
|
if k.startswith(prefix):
|
|
42
|
-
out[k[len_prefix
|
|
42
|
+
out[k[len_prefix:]] = v
|
|
43
43
|
return out
|
|
44
44
|
|
|
45
|
-
def load_with_profiles(
|
|
45
|
+
def load_with_profiles( # type: ignore[override]
|
|
46
46
|
self,
|
|
47
47
|
conf: Any,
|
|
48
48
|
ignore_nonexist: bool = False,
|
|
@@ -8,7 +8,7 @@ from ..caster import (
|
|
|
8
8
|
)
|
|
9
9
|
from . import Loader
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
toml = require_package("rtoml", "tomllib", "tomli")
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class TomlLoader(Loader):
|
|
@@ -24,5 +24,9 @@ class TomlLoader(Loader):
|
|
|
24
24
|
if not self._exists(conf, ignore_nonexist):
|
|
25
25
|
return Diot()
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if toml.__name__ in ("tomli", "tomllib"): # pragma: no cover
|
|
28
|
+
with open(conf, "rb") as f:
|
|
29
|
+
return Diot(toml.load(f))
|
|
30
|
+
|
|
31
|
+
with open(conf, "r") as f: # rtoml
|
|
32
|
+
return Diot(toml.load(f))
|
|
File without changes
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from importlib import import_module
|
|
4
5
|
from types import ModuleType
|
|
5
|
-
from typing import
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
from .exceptions import FormatNotSupported
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from .loaders import Loader
|
|
9
|
+
from .loaders import Loader
|
|
11
10
|
|
|
12
11
|
POOL_KEY = "_SIMPLECONF_POOL"
|
|
13
12
|
META_KEY = "_SIMPLECONF_META"
|
|
@@ -32,36 +31,58 @@ def config_to_ext(conf: Any) -> str:
|
|
|
32
31
|
return out
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
def get_loader(ext: str) ->
|
|
34
|
+
def get_loader(ext: str | Loader) -> Loader:
|
|
36
35
|
"""Get the loader for the extension"""
|
|
36
|
+
if isinstance(ext, Loader):
|
|
37
|
+
return ext
|
|
38
|
+
|
|
37
39
|
if ext == "dict":
|
|
38
40
|
from .loaders.dict import DictLoader
|
|
41
|
+
|
|
39
42
|
return DictLoader()
|
|
40
43
|
if ext == "env":
|
|
41
44
|
from .loaders.env import EnvLoader
|
|
45
|
+
|
|
42
46
|
return EnvLoader()
|
|
43
47
|
if ext == "ini":
|
|
44
48
|
from .loaders.ini import IniLoader
|
|
49
|
+
|
|
45
50
|
return IniLoader()
|
|
46
51
|
if ext == "json":
|
|
47
52
|
from .loaders.json import JsonLoader
|
|
53
|
+
|
|
48
54
|
return JsonLoader()
|
|
49
55
|
if ext == "osenv":
|
|
50
56
|
from .loaders.osenv import OsenvLoader
|
|
57
|
+
|
|
51
58
|
return OsenvLoader()
|
|
52
59
|
if ext == "toml":
|
|
53
60
|
from .loaders.toml import TomlLoader
|
|
61
|
+
|
|
54
62
|
return TomlLoader()
|
|
55
63
|
if ext == "yaml":
|
|
56
64
|
from .loaders.yaml import YamlLoader
|
|
65
|
+
|
|
57
66
|
return YamlLoader()
|
|
58
67
|
|
|
59
68
|
raise FormatNotSupported(f"{ext} is not supported.")
|
|
60
69
|
|
|
61
70
|
|
|
62
|
-
def require_package(package: str) ->
|
|
71
|
+
def require_package(package: str, *fallbacks: str) -> ModuleType:
|
|
63
72
|
"""Require the package and return the module"""
|
|
64
73
|
try:
|
|
65
74
|
return import_module(package)
|
|
66
75
|
except ModuleNotFoundError:
|
|
67
|
-
|
|
76
|
+
for fallback in fallbacks:
|
|
77
|
+
try:
|
|
78
|
+
return import_module(fallback)
|
|
79
|
+
except ModuleNotFoundError:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
if fallbacks:
|
|
83
|
+
raise ImportError(
|
|
84
|
+
f"Neither '{package}' nor its fallbacks "
|
|
85
|
+
f"`{', '.join(fallbacks)}` is installed."
|
|
86
|
+
) from None
|
|
87
|
+
else:
|
|
88
|
+
raise ImportError(f"'{package}' is not installed.") from None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|