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

24 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-21 15:50 +0200

1""" 

2Logic for the TypedConfig inheritable class. 

3""" 

4 

5import typing 

6 

7from .core import T_data, all_annotations, check_type, load_into 

8from .errors import ConfigErrorExtraKey, ConfigErrorInvalidType 

9 

10C = typing.TypeVar("C", bound=typing.Any) 

11 

12 

13class TypedConfig: 

14 """ 

15 Can be used instead of load_into. 

16 """ 

17 

18 @classmethod 

19 def load( 

20 cls: typing.Type[C], data: T_data, key: str = None, init: dict[str, typing.Any] = None, strict: bool = True 

21 ) -> C: 

22 """ 

23 Load a class' config values from the config file. 

24 

25 SomeClass.load(data, ...) = load_into(SomeClass, data, ...). 

26 """ 

27 return load_into(cls, data, key=key, init=init, strict=strict) 

28 

29 def _update(self, _strict: bool = True, _allow_none: bool = False, **values: typing.Any) -> None: 

30 """ 

31 Can be used if .update is overwritten with another value in the config. 

32 """ 

33 annotations = all_annotations(self.__class__) 

34 

35 for key, value in values.items(): 

36 if value is None and not _allow_none: 

37 continue 

38 

39 if _strict and key not in annotations: 

40 raise ConfigErrorExtraKey(cls=self.__class__, key=key, value=value) 

41 

42 if _strict and not check_type(value, annotations[key]) and not (value is None and _allow_none): 

43 raise ConfigErrorInvalidType(expected_type=annotations[key], key=key, value=value) 

44 

45 setattr(self, key, value) 

46 

47 def update(self, _strict: bool = True, _allow_none: bool = False, **values: typing.Any) -> None: 

48 """ 

49 Update values on this config. 

50 

51 Args: 

52 _strict: allow wrong types? 

53 _allow_none: allow None or skip those entries? 

54 **values: key: value pairs in the right types to update. 

55 """ 

56 return self._update(_strict, _allow_none, **values) 

57 

58 def _format(self, string: str) -> str: 

59 """ 

60 Format the config data into a string template. 

61 

62 Replacement for string.format(**config), which is only possible for MutableMappings. 

63 MutableMapping does not work well with our Singleton Metaclass. 

64 """ 

65 return string.format(**self.__dict__) 

66 

67 

68# also expose as separate function: 

69def update(instance: typing.Any, _strict: bool = True, _allow_none: bool = False, **values: typing.Any) -> None: 

70 """ 

71 Update values on a config. 

72 

73 Args: 

74 instance: config instance to update 

75 _strict: allow wrong types? 

76 _allow_none: allow None or skip those entries? 

77 **values: key: value pairs in the right types to update. 

78 """ 

79 return TypedConfig._update(instance, _strict, _allow_none, **values)