pythonwrench 0.6.4__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.
- pythonwrench/__init__.py +490 -0
- pythonwrench/__main__.py +7 -0
- pythonwrench/_core.py +192 -0
- pythonwrench/abc.py +30 -0
- pythonwrench/argparse/__init__.py +81 -0
- pythonwrench/argparse/dataclass_.py +284 -0
- pythonwrench/argparse/parsers.py +619 -0
- pythonwrench/cast.py +247 -0
- pythonwrench/checksum.py +427 -0
- pythonwrench/collections/__init__.py +104 -0
- pythonwrench/collections/collections.py +900 -0
- pythonwrench/collections/prop.py +104 -0
- pythonwrench/collections/reducers.py +330 -0
- pythonwrench/concurrent.py +73 -0
- pythonwrench/csv.py +12 -0
- pythonwrench/dataclasses.py +117 -0
- pythonwrench/datetime.py +17 -0
- pythonwrench/difflib.py +39 -0
- pythonwrench/disk_cache.py +615 -0
- pythonwrench/entrypoints/info.py +44 -0
- pythonwrench/entrypoints/safe_rmdir.py +98 -0
- pythonwrench/entrypoints/tree.py +113 -0
- pythonwrench/enum.py +55 -0
- pythonwrench/functools.py +234 -0
- pythonwrench/hashlib.py +95 -0
- pythonwrench/importlib.py +243 -0
- pythonwrench/inspect.py +69 -0
- pythonwrench/json.py +12 -0
- pythonwrench/jsonl.py +12 -0
- pythonwrench/logging.py +252 -0
- pythonwrench/math.py +107 -0
- pythonwrench/os.py +226 -0
- pythonwrench/pickle.py +12 -0
- pythonwrench/random.py +60 -0
- pythonwrench/re.py +139 -0
- pythonwrench/semver.py +406 -0
- pythonwrench/serialization/__init__.py +70 -0
- pythonwrench/serialization/_core.py +70 -0
- pythonwrench/serialization/csv.py +493 -0
- pythonwrench/serialization/json.py +178 -0
- pythonwrench/serialization/jsonl.py +215 -0
- pythonwrench/serialization/pickle.py +186 -0
- pythonwrench/time.py +34 -0
- pythonwrench/typing/__init__.py +125 -0
- pythonwrench/typing/checks.py +551 -0
- pythonwrench/typing/classes.py +251 -0
- pythonwrench/warnings.py +118 -0
- pythonwrench-0.6.4.dist-info/METADATA +242 -0
- pythonwrench-0.6.4.dist-info/RECORD +52 -0
- pythonwrench-0.6.4.dist-info/WHEEL +4 -0
- pythonwrench-0.6.4.dist-info/entry_points.txt +10 -0
- pythonwrench-0.6.4.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from collections.abc import Iterable as _RuntimeIterable
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from functools import partial
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Callable,
|
|
12
|
+
Iterable,
|
|
13
|
+
List,
|
|
14
|
+
Literal,
|
|
15
|
+
Optional,
|
|
16
|
+
Tuple,
|
|
17
|
+
Type,
|
|
18
|
+
TypeVar,
|
|
19
|
+
Union,
|
|
20
|
+
get_args,
|
|
21
|
+
get_origin,
|
|
22
|
+
overload,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from pythonwrench._core import Predicate
|
|
26
|
+
from pythonwrench.typing.classes import NoneType, UnionType
|
|
27
|
+
from pythonwrench.warnings import deprecated_alias
|
|
28
|
+
|
|
29
|
+
T = TypeVar("T")
|
|
30
|
+
T_Enum = TypeVar("T_Enum", bound=Enum)
|
|
31
|
+
T_Callable = TypeVar("T_Callable", bound=Callable)
|
|
32
|
+
TargetType = Union[
|
|
33
|
+
Type[T],
|
|
34
|
+
UnionType,
|
|
35
|
+
"Type[Literal]",
|
|
36
|
+
"Type[Optional]",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
ListParsing = Literal["argparse", "brackets"]
|
|
40
|
+
HandleException = Literal["return", "raise", "ignore"]
|
|
41
|
+
|
|
42
|
+
DEFAULT_TRUE_VALUES = ("True", "t", "yes", "y", "1")
|
|
43
|
+
DEFAULT_FALSE_VALUES = ("False", "f", "no", "n", "0")
|
|
44
|
+
DEFAULT_NONE_VALUES = ("None", "null")
|
|
45
|
+
|
|
46
|
+
_PARSER_REGISTRY: List[Tuple[Union[TargetType, Predicate], Callable]] = []
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@overload
|
|
50
|
+
def register_parser_fn(
|
|
51
|
+
type_: Union[TargetType[T], Predicate, None],
|
|
52
|
+
fn: None = None,
|
|
53
|
+
) -> Callable[[T_Callable], T_Callable]:
|
|
54
|
+
"""Perform the register parser fn operation."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@overload
|
|
59
|
+
def register_parser_fn(
|
|
60
|
+
type_: Union[TargetType[T], Predicate, None],
|
|
61
|
+
fn: T_Callable,
|
|
62
|
+
) -> T_Callable:
|
|
63
|
+
"""Perform the register parser fn operation."""
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def register_parser_fn(
|
|
68
|
+
type_: Union[TargetType[T], Predicate, None],
|
|
69
|
+
fn: Optional[Callable] = None,
|
|
70
|
+
) -> Callable:
|
|
71
|
+
"""Perform the register parser fn operation."""
|
|
72
|
+
global _PARSER_REGISTRY
|
|
73
|
+
if type_ is None:
|
|
74
|
+
type_ = NoneType
|
|
75
|
+
|
|
76
|
+
if fn is None:
|
|
77
|
+
return partial(register_parser_fn, type_)
|
|
78
|
+
else:
|
|
79
|
+
for type_or_pred, _ in _PARSER_REGISTRY:
|
|
80
|
+
if type_or_pred is type_:
|
|
81
|
+
return fn
|
|
82
|
+
_PARSER_REGISTRY.append((type_, fn)) # type: ignore
|
|
83
|
+
return fn
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def parse_to_type(
|
|
87
|
+
x: str,
|
|
88
|
+
target_type: TargetType[T],
|
|
89
|
+
*,
|
|
90
|
+
case_sensitive: bool = False,
|
|
91
|
+
true_values: Union[str, Iterable[str]] = DEFAULT_TRUE_VALUES,
|
|
92
|
+
false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
|
|
93
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
94
|
+
list_parsing: ListParsing = "argparse",
|
|
95
|
+
handle_exception: HandleException = "raise",
|
|
96
|
+
) -> T:
|
|
97
|
+
"""Convert string values to target type safely. Intended for argparse arguments.
|
|
98
|
+
|
|
99
|
+
- True values: 'True', 'T', 'yes', 'y', '1'.
|
|
100
|
+
- False values: 'False', 'F', 'no', 'n', '0'.
|
|
101
|
+
- None values: 'None', 'null'
|
|
102
|
+
- Other raises ValueError.
|
|
103
|
+
"""
|
|
104
|
+
parse_fn = get_parse_fn(
|
|
105
|
+
target_type,
|
|
106
|
+
case_sensitive=case_sensitive,
|
|
107
|
+
true_values=true_values,
|
|
108
|
+
false_values=false_values,
|
|
109
|
+
none_values=none_values,
|
|
110
|
+
list_parsing=list_parsing,
|
|
111
|
+
handle_exception=handle_exception,
|
|
112
|
+
)
|
|
113
|
+
output = parse_fn(x)
|
|
114
|
+
return output
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_parse_fn(
|
|
118
|
+
type_: TargetType[T],
|
|
119
|
+
*,
|
|
120
|
+
case_sensitive: bool = False,
|
|
121
|
+
true_values: Union[str, Iterable[str]] = DEFAULT_TRUE_VALUES,
|
|
122
|
+
false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
|
|
123
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
124
|
+
list_parsing: ListParsing = "argparse",
|
|
125
|
+
handle_exception: HandleException = "raise",
|
|
126
|
+
) -> Callable[[str], T]:
|
|
127
|
+
"""Returns a callable that convert string value to target type safely.
|
|
128
|
+
|
|
129
|
+
Intended for argparse arguments.
|
|
130
|
+
"""
|
|
131
|
+
kwds = dict(
|
|
132
|
+
case_sensitive=case_sensitive,
|
|
133
|
+
true_values=true_values,
|
|
134
|
+
false_values=false_values,
|
|
135
|
+
none_values=none_values,
|
|
136
|
+
list_parsing=list_parsing,
|
|
137
|
+
handle_exception=handle_exception,
|
|
138
|
+
)
|
|
139
|
+
parse_fn = _search_parse_fn(type_, **kwds)
|
|
140
|
+
|
|
141
|
+
if parse_fn is None:
|
|
142
|
+
msg = f"Invalid argument {type_=}. (no valid type or typing found in registry)"
|
|
143
|
+
raise ValueError(msg)
|
|
144
|
+
|
|
145
|
+
return parse_fn
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _search_parse_fn(type_: TargetType[T], **kwds) -> Optional[Callable[[str], T]]:
|
|
149
|
+
if type_ is None:
|
|
150
|
+
type_ = NoneType
|
|
151
|
+
|
|
152
|
+
parse_fn = None
|
|
153
|
+
for type_or_pred_i, parse_fn_i in _PARSER_REGISTRY:
|
|
154
|
+
if isinstance(type_or_pred_i, type):
|
|
155
|
+
if type_ == type_or_pred_i:
|
|
156
|
+
parse_fn = parse_fn_i
|
|
157
|
+
break
|
|
158
|
+
elif isinstance(type_or_pred_i, Predicate):
|
|
159
|
+
if type_or_pred_i(type_, **kwds) is True:
|
|
160
|
+
parse_fn = partial(parse_fn_i, type_)
|
|
161
|
+
break
|
|
162
|
+
else:
|
|
163
|
+
msg = f"Invalid argument {type_or_pred_i=}. (excepted type or predicate function)"
|
|
164
|
+
raise ValueError(msg)
|
|
165
|
+
|
|
166
|
+
if parse_fn is not None:
|
|
167
|
+
parse_fn = partial(parse_fn, **kwds)
|
|
168
|
+
|
|
169
|
+
return parse_fn
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@register_parser_fn(bool)
|
|
173
|
+
def parse_to_bool(
|
|
174
|
+
x: str,
|
|
175
|
+
*,
|
|
176
|
+
case_sensitive: bool = False,
|
|
177
|
+
true_values: Union[str, Iterable[str]] = DEFAULT_TRUE_VALUES,
|
|
178
|
+
false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
|
|
179
|
+
handle_exception: HandleException = "raise",
|
|
180
|
+
**kwds,
|
|
181
|
+
) -> bool:
|
|
182
|
+
"""Parse to bool."""
|
|
183
|
+
true_values = _sanitize_values(true_values)
|
|
184
|
+
if _str_in(x, true_values, case_sensitive):
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
false_values = _sanitize_values(false_values)
|
|
188
|
+
if _str_in(x, false_values, case_sensitive):
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
values = tuple(true_values + false_values)
|
|
192
|
+
output = ValueError(f"Invalid argument '{x}'. (expected one of {values})")
|
|
193
|
+
return _handle_output(x, handle_exception, output)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@register_parser_fn(float)
|
|
197
|
+
def parse_to_float(
|
|
198
|
+
x: str, handle_exception: HandleException = "raise", **kwds
|
|
199
|
+
) -> float:
|
|
200
|
+
"""Parse to float."""
|
|
201
|
+
try:
|
|
202
|
+
return float(x)
|
|
203
|
+
except ValueError as err:
|
|
204
|
+
return _handle_output(x, handle_exception, err)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@register_parser_fn(int)
|
|
208
|
+
def parse_to_int(x: str, handle_exception: HandleException = "raise", **kwds) -> int:
|
|
209
|
+
"""Parse to int."""
|
|
210
|
+
try:
|
|
211
|
+
return int(x)
|
|
212
|
+
except ValueError as err:
|
|
213
|
+
return _handle_output(x, handle_exception, err)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@register_parser_fn(NoneType)
|
|
217
|
+
def parse_to_none(
|
|
218
|
+
x: str,
|
|
219
|
+
*,
|
|
220
|
+
case_sensitive: bool = False,
|
|
221
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
222
|
+
handle_exception: HandleException = "raise",
|
|
223
|
+
**kwds,
|
|
224
|
+
) -> Union[None, Exception]:
|
|
225
|
+
"""Convert string values to None safely. Intended for argparse arguments.
|
|
226
|
+
|
|
227
|
+
- None values: 'None', 'null'
|
|
228
|
+
- Other raises ValueError.
|
|
229
|
+
"""
|
|
230
|
+
none_values = _sanitize_values(none_values)
|
|
231
|
+
if _str_in(x, none_values, case_sensitive):
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
values = tuple(none_values)
|
|
235
|
+
output = ValueError(f"Invalid argument '{x}'. (expected one of {values})")
|
|
236
|
+
return _handle_output(x, handle_exception, output)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@register_parser_fn(Path)
|
|
240
|
+
def _parse_to_path(x: str, handle_exception: HandleException = "raise", **kwds) -> Path:
|
|
241
|
+
"""Parse to path."""
|
|
242
|
+
try:
|
|
243
|
+
return Path(x)
|
|
244
|
+
except (ValueError, TypeError) as err:
|
|
245
|
+
return _handle_output(x, handle_exception, err)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@register_parser_fn(str)
|
|
249
|
+
def _parse_to_str(x: str, **kwds) -> str:
|
|
250
|
+
"""Parse to str."""
|
|
251
|
+
return x
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _is_enum_type(x: Any, **kwds) -> bool:
|
|
255
|
+
"""Perform the is enum type operation."""
|
|
256
|
+
return isinstance(x, type) and issubclass(x, Enum)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _is_iterable_type_like(x: Any) -> bool:
|
|
260
|
+
"""Perform the is iterable type like operation."""
|
|
261
|
+
return any(xi in (list, Iterable, _RuntimeIterable) for xi in (x, get_origin(x)))
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _is_literal_type(x: Any) -> bool:
|
|
265
|
+
"""Perform the is literal type operation."""
|
|
266
|
+
origin = get_origin(x)
|
|
267
|
+
return origin is Literal
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _is_optional_type(x: Any) -> bool:
|
|
271
|
+
"""Perform the is optional type operation."""
|
|
272
|
+
return getattr(x, "__name__", None) == "Optional"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _is_union_type(x: Any) -> bool:
|
|
276
|
+
"""Perform the is union type operation."""
|
|
277
|
+
origin = get_origin(x)
|
|
278
|
+
return origin == Union or getattr(origin, "__name__", None) in (
|
|
279
|
+
"Union",
|
|
280
|
+
"UnionType",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _is_enum_for_parsing(x: Any, **kwds) -> bool:
|
|
285
|
+
"""Perform the is enum for parsing operation."""
|
|
286
|
+
return _is_enum_type(x)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _is_iterable_type_like_for_parsing(
|
|
290
|
+
x: Any,
|
|
291
|
+
*,
|
|
292
|
+
list_parsing: ListParsing = "argparse",
|
|
293
|
+
**kwds,
|
|
294
|
+
) -> bool:
|
|
295
|
+
"""Perform the is iterable type like for parsing operation."""
|
|
296
|
+
return (list_parsing == "brackets") and _is_iterable_type_like(x)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _is_literal_for_parsing(x: Any, **kwds) -> bool:
|
|
300
|
+
"""Perform the is literal for parsing operation."""
|
|
301
|
+
return _is_literal_type(x)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _is_optional_for_parsing(x: Any, **kwds) -> bool:
|
|
305
|
+
"""Perform the is optional for parsing operation."""
|
|
306
|
+
return _is_optional_type(x)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _is_union_for_parsing(x: Any, **kwds) -> bool:
|
|
310
|
+
"""Perform the is union for parsing operation."""
|
|
311
|
+
return _is_union_type(x)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@register_parser_fn(_is_enum_for_parsing)
|
|
315
|
+
def _parse_to_enum(
|
|
316
|
+
target_type: Type[T_Enum],
|
|
317
|
+
x: str,
|
|
318
|
+
*,
|
|
319
|
+
case_sensitive: bool = False,
|
|
320
|
+
handle_exception: HandleException = "raise",
|
|
321
|
+
**kwds,
|
|
322
|
+
) -> T_Enum:
|
|
323
|
+
"""Parse to enum."""
|
|
324
|
+
for enum_value in target_type:
|
|
325
|
+
candidates = [enum_value.name, str(enum_value.value)]
|
|
326
|
+
if _str_in(x, candidates, case_sensitive):
|
|
327
|
+
return enum_value
|
|
328
|
+
|
|
329
|
+
msg = f"Invalid argument {x=}. (excepted one of {tuple(target_type)})"
|
|
330
|
+
output = ValueError(msg)
|
|
331
|
+
return _handle_output(x, handle_exception, output)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@register_parser_fn(_is_iterable_type_like_for_parsing)
|
|
335
|
+
def _parse_to_list(
|
|
336
|
+
target_type: TargetType[T],
|
|
337
|
+
x: str,
|
|
338
|
+
*,
|
|
339
|
+
list_parsing: ListParsing = "argparse",
|
|
340
|
+
handle_exception: HandleException = "raise",
|
|
341
|
+
**kwds,
|
|
342
|
+
) -> T:
|
|
343
|
+
"""Parse to list."""
|
|
344
|
+
if list_parsing != "brackets":
|
|
345
|
+
msg = f"Cannot convert {x=} to list with {list_parsing=}. (excepted list_parsing='brackets')"
|
|
346
|
+
raise ValueError(msg)
|
|
347
|
+
|
|
348
|
+
args = get_args(target_type)
|
|
349
|
+
|
|
350
|
+
if len(args) == 0:
|
|
351
|
+
target_item_type = str
|
|
352
|
+
elif len(args) == 1:
|
|
353
|
+
target_item_type = args[0]
|
|
354
|
+
else:
|
|
355
|
+
raise ValueError
|
|
356
|
+
|
|
357
|
+
pattern = r"^\s*\[\s*(|.*[^,\s])(|\s*,)\s*\]\s*$"
|
|
358
|
+
if re.match(pattern, x) is None:
|
|
359
|
+
msg = f"Cannot convert value to list: '{x}'. (with {list_parsing=})"
|
|
360
|
+
output = ValueError(msg)
|
|
361
|
+
return _handle_output(x, handle_exception, output)
|
|
362
|
+
|
|
363
|
+
x = re.sub(pattern, r"\1", x)
|
|
364
|
+
if x == "":
|
|
365
|
+
return [] # type: ignore
|
|
366
|
+
|
|
367
|
+
x_list = x.split(",")
|
|
368
|
+
|
|
369
|
+
output = []
|
|
370
|
+
for xi in x_list:
|
|
371
|
+
output_i = parse_to_type(
|
|
372
|
+
xi, target_item_type, handle_exception="return", **kwds
|
|
373
|
+
) # type: ignore
|
|
374
|
+
if isinstance(output_i, Exception):
|
|
375
|
+
output = output_i
|
|
376
|
+
break
|
|
377
|
+
output.append(output_i)
|
|
378
|
+
|
|
379
|
+
return _handle_output(x, handle_exception, output)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@register_parser_fn(_is_literal_for_parsing)
|
|
383
|
+
def _parse_to_literal(
|
|
384
|
+
target_type: TargetType,
|
|
385
|
+
x: str,
|
|
386
|
+
handle_exception: HandleException = "raise",
|
|
387
|
+
**kwds,
|
|
388
|
+
) -> Any:
|
|
389
|
+
"""Parse to literal."""
|
|
390
|
+
args = get_args(target_type)
|
|
391
|
+
literal_types = {type(value) for value in args}
|
|
392
|
+
scalar = _parse_to_one_of(tuple(literal_types), target_type, x, **kwds)
|
|
393
|
+
|
|
394
|
+
if scalar not in args:
|
|
395
|
+
msg = f"Cannot convert {x} to Literal[{', '.join(args)}]"
|
|
396
|
+
output = ValueError(msg)
|
|
397
|
+
else:
|
|
398
|
+
output = scalar
|
|
399
|
+
return _handle_output(x, handle_exception, output)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@register_parser_fn(_is_optional_for_parsing)
|
|
403
|
+
def _parse_to_optional(target_type: TargetType, x: str, **kwds) -> Any:
|
|
404
|
+
"""Parse to optional."""
|
|
405
|
+
args = (NoneType,) + get_args(target_type)
|
|
406
|
+
return _parse_to_one_of(args, target_type, x, **kwds) # type: ignore
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@register_parser_fn(_is_union_for_parsing)
|
|
410
|
+
def _parse_to_union(target_type: TargetType, x: str, **kwds) -> Any:
|
|
411
|
+
"""Parse to union."""
|
|
412
|
+
args = get_args(target_type)
|
|
413
|
+
return _parse_to_one_of(args, target_type, x, **kwds)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _parse_to_one_of(
|
|
417
|
+
target_types: Iterable[TargetType],
|
|
418
|
+
src_type: TargetType,
|
|
419
|
+
x: str,
|
|
420
|
+
*,
|
|
421
|
+
handle_exception: HandleException = "raise",
|
|
422
|
+
**kwds,
|
|
423
|
+
) -> Any:
|
|
424
|
+
"""Parse to one of."""
|
|
425
|
+
|
|
426
|
+
def key_fn(xi: Any) -> int:
|
|
427
|
+
"""Perform the key fn operation."""
|
|
428
|
+
if xi is str:
|
|
429
|
+
return 1
|
|
430
|
+
else:
|
|
431
|
+
return 0
|
|
432
|
+
|
|
433
|
+
target_types = sorted(target_types, key=key_fn)
|
|
434
|
+
|
|
435
|
+
if len(target_types) == 0:
|
|
436
|
+
msg = f"Cannot parse {x}. (expected at least 1 type from {src_type})"
|
|
437
|
+
raise ValueError(msg)
|
|
438
|
+
|
|
439
|
+
for target_type in target_types:
|
|
440
|
+
output = parse_to_type(x, target_type, handle_exception="return", **kwds) # type: ignore
|
|
441
|
+
if not isinstance(output, Exception):
|
|
442
|
+
return _handle_output(x, handle_exception, output)
|
|
443
|
+
|
|
444
|
+
msg = f"Invalid argument {x=}. (cannot be parsed to {src_type})"
|
|
445
|
+
output = ValueError(msg)
|
|
446
|
+
return _handle_output(x, handle_exception, output)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def parse_to_optional_bool(
|
|
450
|
+
x: str,
|
|
451
|
+
*,
|
|
452
|
+
case_sensitive: bool = False,
|
|
453
|
+
true_values: Union[str, Iterable[str]] = DEFAULT_TRUE_VALUES,
|
|
454
|
+
false_values: Union[str, Iterable[str]] = DEFAULT_FALSE_VALUES,
|
|
455
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
456
|
+
**kwds,
|
|
457
|
+
) -> Optional[bool]:
|
|
458
|
+
"""Convert string values to optional bool safely. Intended for argparse arguments.
|
|
459
|
+
|
|
460
|
+
- True values: 'True', 'T', 'yes', 'y', '1'.
|
|
461
|
+
- False values: 'False', 'F', 'no', 'n', '0'.
|
|
462
|
+
- None values: 'None', 'null'
|
|
463
|
+
- Other raises ValueError.
|
|
464
|
+
"""
|
|
465
|
+
return _parse_to_optional(
|
|
466
|
+
Optional[bool],
|
|
467
|
+
x,
|
|
468
|
+
case_sensitive=case_sensitive,
|
|
469
|
+
true_values=true_values,
|
|
470
|
+
false_values=false_values,
|
|
471
|
+
none_values=none_values,
|
|
472
|
+
**kwds,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def parse_to_optional_float(
|
|
477
|
+
x: str,
|
|
478
|
+
*,
|
|
479
|
+
case_sensitive: bool = False,
|
|
480
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
481
|
+
**kwds,
|
|
482
|
+
) -> Optional[float]:
|
|
483
|
+
"""Convert string values to optional float safely. Intended for argparse arguments."""
|
|
484
|
+
return _parse_to_optional(
|
|
485
|
+
Optional[float],
|
|
486
|
+
x,
|
|
487
|
+
case_sensitive=case_sensitive,
|
|
488
|
+
none_values=none_values,
|
|
489
|
+
**kwds,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def parse_to_optional_int(
|
|
494
|
+
x: str,
|
|
495
|
+
*,
|
|
496
|
+
case_sensitive: bool = False,
|
|
497
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
498
|
+
**kwds,
|
|
499
|
+
) -> Optional[int]:
|
|
500
|
+
"""Convert string values to optional int safely. Intended for argparse arguments."""
|
|
501
|
+
return _parse_to_optional(
|
|
502
|
+
Optional[int],
|
|
503
|
+
x,
|
|
504
|
+
case_sensitive=case_sensitive,
|
|
505
|
+
none_values=none_values,
|
|
506
|
+
**kwds,
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def parse_to_optional_str(
|
|
511
|
+
x: str,
|
|
512
|
+
*,
|
|
513
|
+
case_sensitive: bool = False,
|
|
514
|
+
none_values: Union[str, Iterable[str]] = DEFAULT_NONE_VALUES,
|
|
515
|
+
**kwds,
|
|
516
|
+
) -> Optional[str]:
|
|
517
|
+
"""Convert string values to optional str safely. Intended for argparse arguments."""
|
|
518
|
+
return _parse_to_optional(
|
|
519
|
+
Optional[str],
|
|
520
|
+
x,
|
|
521
|
+
case_sensitive=case_sensitive,
|
|
522
|
+
none_values=none_values,
|
|
523
|
+
**kwds,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _handle_output(x: str, handle_exception: HandleException, output: Any) -> Any:
|
|
528
|
+
"""Perform the handle output operation."""
|
|
529
|
+
if not isinstance(output, Exception):
|
|
530
|
+
return output
|
|
531
|
+
elif handle_exception == "ignore":
|
|
532
|
+
return x # type: ignore
|
|
533
|
+
elif handle_exception == "raise":
|
|
534
|
+
raise output
|
|
535
|
+
elif handle_exception == "return":
|
|
536
|
+
return output
|
|
537
|
+
else:
|
|
538
|
+
msg = f"Invalid argument {handle_exception=}. (expected one of {get_args(HandleException)})"
|
|
539
|
+
raise ValueError(msg)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _sanitize_values(values: Union[str, Iterable[str]]) -> List[str]:
|
|
543
|
+
"""Perform the sanitize values operation."""
|
|
544
|
+
if isinstance(values, str):
|
|
545
|
+
values = [values]
|
|
546
|
+
else:
|
|
547
|
+
values = list(values)
|
|
548
|
+
return values
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _str_in(
|
|
552
|
+
x: str, values: Union[List[str], Tuple[str, ...]], case_sensitive: bool
|
|
553
|
+
) -> bool:
|
|
554
|
+
"""Perform the str in operation."""
|
|
555
|
+
if case_sensitive:
|
|
556
|
+
return x in values
|
|
557
|
+
else:
|
|
558
|
+
return x.lower() in map(str.lower, values)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
# ALIASES
|
|
562
|
+
@deprecated_alias(get_parse_fn)
|
|
563
|
+
def parse_to(*args, **kwds):
|
|
564
|
+
"""Parse to."""
|
|
565
|
+
...
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
@deprecated_alias(parse_to_type)
|
|
569
|
+
def str_to_type(*args, **kwds):
|
|
570
|
+
"""Perform the str to type operation."""
|
|
571
|
+
...
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
@deprecated_alias(parse_to_bool)
|
|
575
|
+
def str_to_bool(*args, **kwds):
|
|
576
|
+
"""Perform the str to bool operation."""
|
|
577
|
+
...
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
@deprecated_alias(parse_to_float)
|
|
581
|
+
def str_to_float(*args, **kwds):
|
|
582
|
+
"""Perform the str to float operation."""
|
|
583
|
+
...
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
@deprecated_alias(parse_to_int)
|
|
587
|
+
def str_to_int(*args, **kwds):
|
|
588
|
+
"""Perform the str to int operation."""
|
|
589
|
+
...
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
@deprecated_alias(parse_to_none)
|
|
593
|
+
def str_to_none(*args, **kwds):
|
|
594
|
+
"""Perform the str to none operation."""
|
|
595
|
+
...
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
@deprecated_alias(parse_to_optional_bool)
|
|
599
|
+
def str_to_optional_bool(*args, **kwds):
|
|
600
|
+
"""Perform the str to optional bool operation."""
|
|
601
|
+
...
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
@deprecated_alias(parse_to_optional_float)
|
|
605
|
+
def str_to_optional_float(*args, **kwds):
|
|
606
|
+
"""Perform the str to optional float operation."""
|
|
607
|
+
...
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
@deprecated_alias(parse_to_optional_int)
|
|
611
|
+
def str_to_optional_int(*args, **kwds):
|
|
612
|
+
"""Perform the str to optional int operation."""
|
|
613
|
+
...
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
@deprecated_alias(parse_to_optional_str)
|
|
617
|
+
def str_to_optional_str(*args, **kwds):
|
|
618
|
+
"""Perform the str to optional str operation."""
|
|
619
|
+
...
|