Coverage for src/configuraptor/type_converters.py: 100%

1"""

2 Register from-to relationship between types, used with load_into(..., convert_types=True).

3"""

4

5 import types

6 import typing

7 from typing import Any

8

9 T = typing.TypeVar("T")

10

11 Wrapped = typing.Callable[[Any], Any]

12 Wrapper = typing.Callable[[Wrapped], Wrapped]

13

14 CONVERTERS = {}

15

16

17 def 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.

20

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!

23

24 @register_converter(str, bool)

25 def str_to_bool(value: str) -> bool:

26 ...

27

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,)

33

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

39

40 return wrapper

41

42

43@register_converter(str, bool)

44 def str_to_bool(value: str) -> bool:

45 """

46 Used by convert_between, usually for .env loads.

47

48 Example:

49 SOME_VALUE=TRUE -> True

50 SOME_VALUE=1 -> True

51 SOME_VALUE=Yes -> True

52

53 SOME_VALUE -> None

54 SOME_VALUE=NOpe -> False

55

56 SOME_VALUE=Unrelated -> Error

57 """

58 if not value:

59 return False

60

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.")

69

70

71@register_converter(str, (types.NoneType, None))

72 def 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