Coverage for src/configuraptor/type_converters.py: 100%
33 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-09 20:07 +0100
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-09 20:07 +0100
1"""
2Register from-to relationship between types, used with load_into(..., convert_types=True).
3"""
5import types
6import typing
7from typing import Any
9T = typing.TypeVar("T")
11Wrapped = typing.Callable[[Any], Any]
12Wrapper = typing.Callable[[Wrapped], Wrapped]
14CONVERTERS = {}
17def register_converter(from_type: type | tuple[type], to_type: type | None | tuple[type | None, ...]) -> Wrapper:
18 """
19 Register a custom converter that converts from `from_type` to `to_type` using custom logic.
21 `from_type` and `to_type` can both be tuples of types,
22 but make sure the `to_type`s are compatible with eachother and your method's return value!
24 @register_converter(str, bool)
25 def str_to_bool(value: str) -> bool:
26 ...
28 """
29 if not isinstance(from_type, tuple):
30 from_type = (from_type,)
31 if not isinstance(to_type, tuple):
32 to_type = (to_type,)
34 def wrapper(func: Wrapped) -> Wrapped:
35 for _from in from_type:
36 for _to in to_type:
37 CONVERTERS[(_from, _to)] = func
38 return func
40 return wrapper
43@register_converter(str, bool)
44def str_to_bool(value: str) -> bool:
45 """
46 Used by convert_between, usually for .env loads.
48 Example:
49 SOME_VALUE=TRUE -> True
50 SOME_VALUE=1 -> True
51 SOME_VALUE=Yes -> True
53 SOME_VALUE -> None
54 SOME_VALUE=NOpe -> False
56 SOME_VALUE=Unrelated -> Error
57 """
58 if not value:
59 return False
61 first_letter = value[0].lower()
62 # yes, true, 1
63 if first_letter in {"y", "t", "1"}:
64 return True
65 elif first_letter in {"n", "f", "0"}:
66 return False
67 else:
68 raise ValueError("Not booly.")
71@register_converter(str, (types.NoneType, None))
72def str_to_none(value: str) -> typing.Optional[str]:
73 """
74 Convert a string value of null/none to None, or keep the original string otherwise.
75 """
76 if value.lower() in {"", "null", "none"}:
77 return None
78 else:
79 return value