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

33 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2025-01-09 20:20 +0100

1""" 

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

3""" 

4 

5import types 

6import typing 

7from typing import Any 

8 

9T = typing.TypeVar("T") 

10 

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

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

13 

14CONVERTERS = {} 

15 

16 

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. 

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) 

44def 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)) 

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