thds.core 1.32.20250219165944__py3-none-any.whl → 1.32.20250219172103__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.
Potentially problematic release.
This version of thds.core might be problematic. Click here for more details.
- thds/core/config.py +74 -43
- thds/core/files.py +1 -1
- thds/core/meta.json +3 -3
- thds/core/source/src.py +15 -1
- {thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/METADATA +1 -1
- {thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/RECORD +9 -9
- {thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/WHEEL +0 -0
- {thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/entry_points.txt +0 -0
- {thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/top_level.txt +0 -0
thds/core/config.py
CHANGED
|
@@ -94,6 +94,14 @@ def _type_parser(default: T) -> ty.Callable[[ty.Any], T]:
|
|
|
94
94
|
return lambda x: x # we can't infer a type parser, so we'll return the default no-op parser.
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
class ConfigRegistry(ty.Dict[str, "ConfigItem"]): # noqa: B903
|
|
98
|
+
def __init__(self, name: str):
|
|
99
|
+
self.name = name
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
_DEFAULT_REGISTRY = ConfigRegistry("default")
|
|
103
|
+
|
|
104
|
+
|
|
97
105
|
class ConfigItem(ty.Generic[T]):
|
|
98
106
|
"""Should only ever be constructed at a module level."""
|
|
99
107
|
|
|
@@ -106,12 +114,17 @@ class ConfigItem(ty.Generic[T]):
|
|
|
106
114
|
*,
|
|
107
115
|
parse: ty.Optional[ty.Callable[[ty.Any], T]] = None,
|
|
108
116
|
secret: bool = False,
|
|
117
|
+
registry: ConfigRegistry = _DEFAULT_REGISTRY,
|
|
118
|
+
name_transform: ty.Callable[[str], str] = _fullname,
|
|
109
119
|
):
|
|
120
|
+
"""parse should be an idempotent parser. In other words, parse(parse(x)) == parse(x)"""
|
|
110
121
|
self.secret = secret
|
|
111
|
-
name =
|
|
112
|
-
if name in
|
|
113
|
-
raise ConfigNameCollisionError(
|
|
114
|
-
|
|
122
|
+
name = name_transform(name)
|
|
123
|
+
if name in registry:
|
|
124
|
+
raise ConfigNameCollisionError(
|
|
125
|
+
f"Config item {name} has already been registered in {registry.name}!"
|
|
126
|
+
)
|
|
127
|
+
registry[name] = self
|
|
115
128
|
self.name = name
|
|
116
129
|
self.parse = parse or _type_parser(default)
|
|
117
130
|
raw_resolved_global = _getenv(name, secret=secret)
|
|
@@ -156,6 +169,9 @@ class ConfigItem(ty.Generic[T]):
|
|
|
156
169
|
raise UnconfiguredError(f"Config item '{self.name}' has not been configured!")
|
|
157
170
|
return self.global_value
|
|
158
171
|
|
|
172
|
+
def __repr__(self) -> str:
|
|
173
|
+
return f"ConfigItem('{self.name}', {self()})"
|
|
174
|
+
|
|
159
175
|
|
|
160
176
|
def tobool(s_or_b: ty.Union[str, bool]) -> bool:
|
|
161
177
|
"""A reasonable implementation that we could expand in the future."""
|
|
@@ -171,55 +187,70 @@ item = ConfigItem
|
|
|
171
187
|
# a short alias
|
|
172
188
|
|
|
173
189
|
|
|
174
|
-
|
|
190
|
+
def config_by_name(
|
|
191
|
+
name: str,
|
|
192
|
+
registry: ConfigRegistry = _DEFAULT_REGISTRY,
|
|
193
|
+
) -> ConfigItem:
|
|
194
|
+
"""This is a dynamic interface - in general, prefer accessing the ConfigItem object directly."""
|
|
195
|
+
return registry[_fullname(name)]
|
|
175
196
|
|
|
176
197
|
|
|
177
|
-
def
|
|
178
|
-
"""This is a
|
|
179
|
-
|
|
198
|
+
def flatten_config(config: ty.Mapping[str, ty.Any]) -> ty.Dict[str, ty.Any]:
|
|
199
|
+
"""This is a helper function to flatten a nested configuration dictionary."""
|
|
200
|
+
flat = dict()
|
|
201
|
+
for key, value in config.items():
|
|
202
|
+
if isinstance(value, dict):
|
|
203
|
+
for subkey, subvalue in flatten_config(value).items():
|
|
204
|
+
flat[f"{key}.{subkey}"] = subvalue
|
|
205
|
+
else:
|
|
206
|
+
flat[key] = value
|
|
207
|
+
return flat
|
|
180
208
|
|
|
181
209
|
|
|
182
|
-
def set_global_defaults(
|
|
210
|
+
def set_global_defaults(
|
|
211
|
+
config: ty.Mapping[str, ty.Any],
|
|
212
|
+
registry: ConfigRegistry = _DEFAULT_REGISTRY,
|
|
213
|
+
):
|
|
183
214
|
"""Any config-file parser can create a dictionary of only the
|
|
184
215
|
items it managed to read, and then all of those can be set at once
|
|
185
216
|
via this function.
|
|
186
217
|
"""
|
|
187
|
-
|
|
188
|
-
|
|
218
|
+
flat_config = flatten_config(config)
|
|
219
|
+
for name, value in flat_config.items():
|
|
220
|
+
try:
|
|
221
|
+
config_item = registry[name]
|
|
222
|
+
config_item.set_global(config_item.parse(value))
|
|
223
|
+
except KeyError:
|
|
224
|
+
# try directly importing a module - this is only best-effort and will not work
|
|
225
|
+
# if you did not follow standard configuration naming conventions.
|
|
226
|
+
import importlib
|
|
227
|
+
|
|
228
|
+
maybe_module_name = ".".join(name.split(".")[:-1])
|
|
229
|
+
|
|
189
230
|
try:
|
|
190
|
-
|
|
191
|
-
config_item.set_global(config_item.parse(value))
|
|
192
|
-
except KeyError:
|
|
193
|
-
# try directly importing a module - this is only best-effort and will not work
|
|
194
|
-
# if you did not follow standard configuration naming conventions.
|
|
195
|
-
import importlib
|
|
196
|
-
|
|
197
|
-
maybe_module_name = ".".join(name.split(".")[:-1])
|
|
231
|
+
importlib.import_module(maybe_module_name)
|
|
198
232
|
try:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def get_all_config() -> ty.Dict[str, ty.Any]:
|
|
222
|
-
return {k: v() if not v.secret else "***SECRET***" for k, v in _REGISTRY.items()}
|
|
233
|
+
config_item = registry[name]
|
|
234
|
+
config_item.set_global(config_item.parse(value))
|
|
235
|
+
except KeyError as kerr:
|
|
236
|
+
raise KeyError(
|
|
237
|
+
f"Config item {name} is not registered"
|
|
238
|
+
f" and no module with the name {maybe_module_name} was importable."
|
|
239
|
+
" Please double-check your configuration."
|
|
240
|
+
) from kerr
|
|
241
|
+
except ModuleNotFoundError:
|
|
242
|
+
# create a new, dynamic config item that will only be accessible via
|
|
243
|
+
# its name.
|
|
244
|
+
ConfigItem(
|
|
245
|
+
name, value, registry=registry
|
|
246
|
+
) # return value not needed since it self-registers.
|
|
247
|
+
getLogger(__name__).debug(
|
|
248
|
+
"Created dynamic config item '%s' with value '%s'", name, value
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def get_all_config(registry: ty.Dict[str, ConfigItem] = _DEFAULT_REGISTRY) -> ty.Dict[str, ty.Any]:
|
|
253
|
+
return {k: v() if not v.secret else "***SECRET***" for k, v in registry.items()}
|
|
223
254
|
|
|
224
255
|
|
|
225
256
|
def show_config_cli():
|
thds/core/files.py
CHANGED
|
@@ -36,7 +36,7 @@ def path_from_uri(uri: str) -> Path:
|
|
|
36
36
|
str_path = remove_file_scheme(uri)
|
|
37
37
|
if not str_path:
|
|
38
38
|
raise ValueError('Cannot convert an empty string to a Path. Did you mean to use "."?')
|
|
39
|
-
return Path(str_path)
|
|
39
|
+
return Path(str_path).expanduser().resolve()
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def to_uri(path: Path) -> str:
|
thds/core/meta.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"git_commit": "affac0a04726de27f8065525d193df4cd6376b9c",
|
|
3
|
-
"git_branch": "
|
|
3
|
+
"git_branch": "task/dbxtend/use-pure-magic",
|
|
4
4
|
"git_is_clean": true,
|
|
5
|
-
"pyproject_version": "1.32.
|
|
6
|
-
"thds_user": "
|
|
5
|
+
"pyproject_version": "1.32.20250219172103",
|
|
6
|
+
"thds_user": "peter.gaultney",
|
|
7
7
|
"misc": {}
|
|
8
8
|
}
|
thds/core/source/src.py
CHANGED
|
@@ -3,7 +3,7 @@ import typing as ty
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from .. import hashing
|
|
6
|
+
from .. import hashing, types
|
|
7
7
|
from . import _download
|
|
8
8
|
|
|
9
9
|
|
|
@@ -84,3 +84,17 @@ class Source(os.PathLike):
|
|
|
84
84
|
|
|
85
85
|
def __fspath__(self) -> str:
|
|
86
86
|
return os.fspath(self.path())
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def from_file(
|
|
90
|
+
filename: types.StrOrPath, hash: ty.Optional[hashing.Hash] = None, uri: str = ""
|
|
91
|
+
) -> "Source":
|
|
92
|
+
from ._construct import from_file
|
|
93
|
+
|
|
94
|
+
return from_file(filename, hash, uri)
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def from_uri(uri: str, hash: ty.Optional[hashing.Hash] = None) -> "Source":
|
|
98
|
+
from ._construct import from_uri
|
|
99
|
+
|
|
100
|
+
return from_uri(uri, hash)
|
|
@@ -4,12 +4,12 @@ thds/core/cache.py,sha256=nL0oAyZrhPqyBBLevnOWSWVoEBrftaG3aE6Qq6tvmAA,7153
|
|
|
4
4
|
thds/core/calgitver.py,sha256=HklIz-SczK92Vm2rXtTSDiVxAcxUW_GPVCRRGt4BmBA,2324
|
|
5
5
|
thds/core/cm.py,sha256=WZB8eQU0DaBYj9s97nc3PuCtai9guovfyiQH68zhLzY,1086
|
|
6
6
|
thds/core/concurrency.py,sha256=NQunF_tJ_z8cfVyhzkTPlb-nZrgu-vIk9_3XffgscKQ,3520
|
|
7
|
-
thds/core/config.py,sha256=
|
|
7
|
+
thds/core/config.py,sha256=VWymw6pqPRvX7wwsJ0Y-D2gLoCclAHhARmTnuUw7kb0,10014
|
|
8
8
|
thds/core/decos.py,sha256=VpFTKTArXepICxN4U8C8J6Z5KDq-yVjFZQzqs2jeVAk,1341
|
|
9
9
|
thds/core/dict_utils.py,sha256=MAVkGJg4KQN1UGBLEKuPdQucZaXg_jJakujQ-GUrYzw,6471
|
|
10
10
|
thds/core/env.py,sha256=M36CYkPZ5AUf_-n8EqjsMGwWOzaKEn0KgRwnqUK7jS4,1094
|
|
11
11
|
thds/core/exit_after.py,sha256=0lz63nz2NTiIdyBDYyRa9bQShxQKe7eISy8VhXeW4HU,3485
|
|
12
|
-
thds/core/files.py,sha256=
|
|
12
|
+
thds/core/files.py,sha256=NJlPXj7BejKd_Pa06MOywVv_YapT4bVedfsJHrWX8nI,4579
|
|
13
13
|
thds/core/fretry.py,sha256=Tui2q6vXV6c7mjTa1czLrXiugHUEwQp-sZdiwXfxvmM,3829
|
|
14
14
|
thds/core/generators.py,sha256=rcdFpPj0NMJWSaSZTnBfTeZxTTORNB633Lng-BW1284,1939
|
|
15
15
|
thds/core/git.py,sha256=I6kaEvwcvVxCLYHhTTfnHle-GkmgOR9_fHs03QxgBfI,2792
|
|
@@ -24,7 +24,7 @@ thds/core/lazy.py,sha256=e1WvG4LsbEydV0igEr_Vl1cq05zlQNIE8MFYT90yglE,3289
|
|
|
24
24
|
thds/core/link.py,sha256=kmFJIFvEZc16-7S7IGvtTpzwl3VuvFl3yPlE6WJJ03w,5404
|
|
25
25
|
thds/core/logical_root.py,sha256=gWkIYRv9kNQfzbpxJaYiwNXVz1neZ2NvnvProtOn9d8,1399
|
|
26
26
|
thds/core/merge_args.py,sha256=7oj7dtO1-XVkfTM3aBlq3QlZbo8tb6X7E3EVIR-60t8,5781
|
|
27
|
-
thds/core/meta.json,sha256=
|
|
27
|
+
thds/core/meta.json,sha256=dKSxAvJfyiieovIECa_e5kibVmRg29mmOmqqHGS5Mfs,227
|
|
28
28
|
thds/core/meta.py,sha256=IPLAKrH06HooPMNf5FeqJvUcM-JljTGXddrAQ5oAX8E,16896
|
|
29
29
|
thds/core/parallel.py,sha256=HXAn9aIYqNE5rnRN5ypxR6CUucdfzE5T5rJ_MUv-pFk,7590
|
|
30
30
|
thds/core/pickle_visit.py,sha256=QNMWIi5buvk2zsvx1-D-FKL7tkrFUFDs387vxgGebgU,833
|
|
@@ -50,7 +50,7 @@ thds/core/source/__init__.py,sha256=RiaUHNunoaw4XJUrwR5vJzSS6HGxOUKUONR_ipX5654,
|
|
|
50
50
|
thds/core/source/_construct.py,sha256=klN6-fSJrsbbUhp92wzhJcF73h_PKKJItNLC__vwlIs,3122
|
|
51
51
|
thds/core/source/_download.py,sha256=pUhkphHdB7y4ZpxZZ6ITIS5giXMHuRf420yYAJwx6aE,2924
|
|
52
52
|
thds/core/source/serde.py,sha256=wXCfuv_Dv3QvJJr-uebGmTrfhCU_1a8VX3VJnXhVHfU,3539
|
|
53
|
-
thds/core/source/src.py,sha256=
|
|
53
|
+
thds/core/source/src.py,sha256=9A_8kSBUc5k6OLAYe5EW_VogpXFIqofow7Rxl8xv-eg,4559
|
|
54
54
|
thds/core/source/tree.py,sha256=vjAqnQXGE0XiI0WvlLyXGqEAZbyjq6XmdUeWAR0HI4M,4144
|
|
55
55
|
thds/core/sqlite/__init__.py,sha256=tDMzuO76qTtckJHldPQ6nPZ6kcvhhoJrVuuW42JtaSQ,606
|
|
56
56
|
thds/core/sqlite/connect.py,sha256=l4QaSAI8RjP7Qh2FjmJ3EwRgfGf65Z3-LjtC9ocHM_U,977
|
|
@@ -67,8 +67,8 @@ thds/core/sqlite/structured.py,sha256=swCbDoyVT6cE7Kl79Wh_rg5Z1-yrUDJbiVJF4bjset
|
|
|
67
67
|
thds/core/sqlite/types.py,sha256=oUkfoKRYNGDPZRk29s09rc9ha3SCk2SKr_K6WKebBFs,1308
|
|
68
68
|
thds/core/sqlite/upsert.py,sha256=BmKK6fsGVedt43iY-Lp7dnAu8aJ1e9CYlPVEQR2pMj4,5827
|
|
69
69
|
thds/core/sqlite/write.py,sha256=z0219vDkQDCnsV0WLvsj94keItr7H4j7Y_evbcoBrWU,3458
|
|
70
|
-
thds.core-1.32.
|
|
71
|
-
thds.core-1.32.
|
|
72
|
-
thds.core-1.32.
|
|
73
|
-
thds.core-1.32.
|
|
74
|
-
thds.core-1.32.
|
|
70
|
+
thds.core-1.32.20250219172103.dist-info/METADATA,sha256=BVFThCu8DvjqINHhfNv2Ztt8jraXEiQKAj0h3wLzQdg,2123
|
|
71
|
+
thds.core-1.32.20250219172103.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
72
|
+
thds.core-1.32.20250219172103.dist-info/entry_points.txt,sha256=bOCOVhKZv7azF3FvaWX6uxE6yrjK6FcjqhtxXvLiFY8,161
|
|
73
|
+
thds.core-1.32.20250219172103.dist-info/top_level.txt,sha256=LTZaE5SkWJwv9bwOlMbIhiS-JWQEEIcjVYnJrt-CriY,5
|
|
74
|
+
thds.core-1.32.20250219172103.dist-info/RECORD,,
|
|
File without changes
|
{thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{thds.core-1.32.20250219165944.dist-info → thds.core-1.32.20250219172103.dist-info}/top_level.txt
RENAMED
|
File without changes
|