dycw-utilities 0.166.16__py3-none-any.whl → 0.166.17__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.166.16
3
+ Version: 0.166.17
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -18,13 +18,13 @@ Requires-Dist: pytest-cov<6.3,>=6.2.1; extra == 'test'
18
18
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
19
19
  Requires-Dist: pytest-lazy-fixtures<1.4,>=1.3.4; extra == 'test'
20
20
  Requires-Dist: pytest-randomly<3.17,>=3.16.0; extra == 'test'
21
- Requires-Dist: pytest-regressions<2.9,>=2.8.2; extra == 'test'
21
+ Requires-Dist: pytest-regressions<2.9,>=2.8.3; extra == 'test'
22
22
  Requires-Dist: pytest-repeat<0.10,>=0.9.4; extra == 'test'
23
23
  Requires-Dist: pytest-rerunfailures<16.1,>=16.0.1; extra == 'test'
24
24
  Requires-Dist: pytest-rng<1.1,>=1.0.0; extra == 'test'
25
25
  Requires-Dist: pytest-timeout<2.5,>=2.4.0; extra == 'test'
26
26
  Requires-Dist: pytest-xdist<3.9,>=3.8.0; extra == 'test'
27
- Requires-Dist: pytest<8.5,>=8.4.1; extra == 'test'
27
+ Requires-Dist: pytest<8.5,>=8.4.2; extra == 'test'
28
28
  Requires-Dist: testbook<0.5,>=0.4.2; extra == 'test'
29
29
  Description-Content-Type: text/markdown
30
30
 
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=FemsKd9UtEJaP-BgHzGaJoLRpH-_mhv6Y7fl8XXOKCY,61
1
+ utilities/__init__.py,sha256=zjtoeFc-ojZa5CJiIUK9ZCv9HTNHqODG_fmFppulLvA,61
2
2
  utilities/aeventkit.py,sha256=ddoleSwW9zdc2tjX5Ge0pMKtYwV_JMxhHYOxnWX2AGM,12609
3
3
  utilities/altair.py,sha256=nHdpWt8ZwdUwRQN970MvHd5bRWokNqzHcZQEdSHKRuE,9033
4
4
  utilities/asyncio.py,sha256=PUedzQ5deqlSECQ33sam9cRzI9TnygHz3FdOqWJWPTM,15288
@@ -53,8 +53,8 @@ utilities/pottery.py,sha256=ggMN72Y7wx7Js8VN6eyNyodpm8TIYqZHGghkDPXIVWk,3949
53
53
  utilities/pqdm.py,sha256=idv2seRVP2f6NeSfpeEnT5A-tQezaHZKDyeu16g2-0E,3091
54
54
  utilities/psutil.py,sha256=KUlu4lrUw9Zg1V7ZGetpWpGb9DB8l_SSDWGbANFNCPU,2104
55
55
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
- utilities/pydantic_settings.py,sha256=oUosQ7KExpXTC1PioIc4bju6C6Yj6gMZQLBpOKRL4Xc,2166
57
- utilities/pydantic_settings_sops.py,sha256=3RGZbRgfJjAxveMUNpdf7TNBtGuEBYZZ5_TkIxf1mNE,1194
56
+ utilities/pydantic_settings.py,sha256=xcm8TUa3Ok3kWuC7RojwKNimiO333iugbw1doqcKv7Q,3375
57
+ utilities/pydantic_settings_sops.py,sha256=LWI3NTQP8hSaGkoRbahyqaalF0HuBg7o63-p7boHgpQ,1119
58
58
  utilities/pyinstrument.py,sha256=hnXaL-4HE7wWBI5JKaPfYTpsrXe68YiuZKahHV0VJn8,841
59
59
  utilities/pytest.py,sha256=Pu8jmeNQq659uQmZsmFj6lb0sHMDsNN3fcd6M21J-ww,7723
60
60
  utilities/pytest_regressions.py,sha256=ocjHTtfOeiGfQAKIei8pKNd61sxN9dawrJJ9gPt2wzA,4097
@@ -91,8 +91,8 @@ utilities/zoneinfo.py,sha256=tdIScrTB2-B-LH0ukb1HUXKooLknOfJNwHk10MuMYvA,3619
91
91
  utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
92
92
  utilities/pytest_plugins/pytest_randomly.py,sha256=B1qYVlExGOxTywq2r1SMi5o7btHLk2PNdY_b1p98dkE,409
93
93
  utilities/pytest_plugins/pytest_regressions.py,sha256=9v8kAXDM2ycIXJBimoiF4EgrwbUvxTycFWJiGR_GHhM,1466
94
- dycw_utilities-0.166.16.dist-info/METADATA,sha256=mRIYx_yS0XbJdgF91-6wfLFf0a8T4bRtJC_v3cLoFO4,1702
95
- dycw_utilities-0.166.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
- dycw_utilities-0.166.16.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
97
- dycw_utilities-0.166.16.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
98
- dycw_utilities-0.166.16.dist-info/RECORD,,
94
+ dycw_utilities-0.166.17.dist-info/METADATA,sha256=ubc8X0TWb00B1HdApe5jrnnYdi2B8hpaCodXZwX1HoM,1702
95
+ dycw_utilities-0.166.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
+ dycw_utilities-0.166.17.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
97
+ dycw_utilities-0.166.17.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
98
+ dycw_utilities-0.166.17.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.166.16"
3
+ __version__ = "0.166.17"
@@ -1,6 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, ClassVar, override
3
+ from functools import reduce
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING, Any, ClassVar, assert_never, override
4
6
 
5
7
  from pydantic_settings import (
6
8
  BaseSettings,
@@ -10,22 +12,28 @@ from pydantic_settings import (
10
12
  TomlConfigSettingsSource,
11
13
  YamlConfigSettingsSource,
12
14
  )
15
+ from pydantic_settings.sources import DEFAULT_PATH
13
16
 
14
17
  from utilities.iterables import always_iterable
15
18
 
16
19
  if TYPE_CHECKING:
17
- from collections.abc import Iterator
20
+ from collections.abc import Iterator, Sequence
18
21
 
19
- from utilities.types import MaybeIterable, PathLike
22
+ from pydantic_settings.sources import PathType
23
+
24
+ from utilities.types import MaybeSequenceStr, PathLike
25
+
26
+
27
+ type PathLikeOrWithSection = PathLike | tuple[PathLike, MaybeSequenceStr]
20
28
 
21
29
 
22
30
  class CustomBaseSettings(BaseSettings):
23
31
  """Base settings for loading JSON files."""
24
32
 
25
33
  # paths
26
- json_files: ClassVar[MaybeIterable[PathLike]] = ()
27
- toml_files: ClassVar[MaybeIterable[PathLike]] = ()
28
- yaml_files: ClassVar[MaybeIterable[PathLike]] = ()
34
+ json_files: ClassVar[Sequence[PathLike]] = []
35
+ toml_files: ClassVar[Sequence[PathLikeOrWithSection]] = []
36
+ yaml_files: ClassVar[Sequence[PathLike]] = []
29
37
 
30
38
  # config
31
39
  model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
@@ -53,11 +61,19 @@ class CustomBaseSettings(BaseSettings):
53
61
  /,
54
62
  ) -> Iterator[PydanticBaseSettingsSource]:
55
63
  yield env_settings
56
- for file in always_iterable(cls.json_files):
64
+ for file in cls.json_files:
57
65
  yield JsonConfigSettingsSource(settings_cls, json_file=file)
58
- for file in always_iterable(cls.toml_files):
59
- yield TomlConfigSettingsSource(settings_cls, toml_file=file)
60
- for file in always_iterable(cls.yaml_files):
66
+ for path_or_pair in cls.toml_files:
67
+ match path_or_pair:
68
+ case Path() | str() as file:
69
+ yield TomlConfigSettingsSource(settings_cls, toml_file=file)
70
+ case Path() | str() as file, str() | list() | tuple() as section:
71
+ yield TomlConfigSectionSettingsSource(
72
+ settings_cls, toml_file=file, section=section
73
+ )
74
+ case never:
75
+ assert_never(never)
76
+ for file in cls.yaml_files:
61
77
  yield YamlConfigSettingsSource(settings_cls, yaml_file=file)
62
78
 
63
79
 
@@ -66,4 +82,25 @@ def load_settings[T: BaseSettings](cls: type[T], /) -> T:
66
82
  return cls()
67
83
 
68
84
 
69
- __all__ = ["CustomBaseSettings", "load_settings"]
85
+ class TomlConfigSectionSettingsSource(TomlConfigSettingsSource):
86
+ @override
87
+ def __init__(
88
+ self,
89
+ settings_cls: type[BaseSettings],
90
+ toml_file: PathType | None = DEFAULT_PATH,
91
+ *,
92
+ section: MaybeSequenceStr,
93
+ ) -> None:
94
+ super().__init__(settings_cls, toml_file=toml_file)
95
+ self.section = section
96
+
97
+ @override
98
+ def __call__(self) -> dict[str, Any]:
99
+ return reduce(
100
+ lambda acc, el: acc.get(el, {}),
101
+ always_iterable(self.section),
102
+ super().__call__(),
103
+ )
104
+
105
+
106
+ __all__ = ["CustomBaseSettings", "TomlConfigSectionSettingsSource", "load_settings"]
@@ -4,22 +4,21 @@ from typing import TYPE_CHECKING, ClassVar, override
4
4
 
5
5
  from pydantic_settings_sops import SOPSConfigSettingsSource
6
6
 
7
- from utilities.iterables import always_iterable
8
7
  from utilities.pydantic_settings import CustomBaseSettings
9
8
 
10
9
  if TYPE_CHECKING:
11
- from collections.abc import Iterator
10
+ from collections.abc import Iterator, Sequence
12
11
 
13
12
  from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
14
13
 
15
- from utilities.types import MaybeIterable, PathLike
14
+ from utilities.types import PathLike
16
15
 
17
16
 
18
17
  class SopsBaseSettings(CustomBaseSettings):
19
18
  """Base settings for loading secrets using `sops/age`."""
20
19
 
21
20
  # paths
22
- secret_files: ClassVar[MaybeIterable[PathLike]] = ()
21
+ secret_files: ClassVar[Sequence[PathLike]] = ()
23
22
 
24
23
  @classmethod
25
24
  @override
@@ -30,7 +29,7 @@ class SopsBaseSettings(CustomBaseSettings):
30
29
  /,
31
30
  ) -> Iterator[PydanticBaseSettingsSource]:
32
31
  yield from super()._yield_base_settings_sources(settings_cls, env_settings)
33
- for file in always_iterable(cls.secret_files):
32
+ for file in cls.secret_files:
34
33
  yield SOPSConfigSettingsSource(
35
34
  settings_cls, # pyright: ignore[reportArgumentType],
36
35
  json_file=file,