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

38 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-11-07 15:36 +0100

1""" 

2Method to dump classes to other formats. 

3""" 

4 

5import json 

6import typing 

7 

8import tomli_w 

9import yaml 

10 

11from .helpers import camel_to_snake, instance_of_custom_class, is_custom_class 

12from .loaders.register import register_dumper 

13 

14if typing.TYPE_CHECKING: # pragma: no cover 

15 from .binary_config import BinaryConfig 

16 

17 

18@register_dumper("dict") 

19def asdict(inst: typing.Any, _level: int = 0, /, with_top_level_key: bool = True) -> dict[str, typing.Any]: 

20 """ 

21 Dump a config instance to a dictionary (recursively). 

22 """ 

23 data: dict[str, typing.Any] = {} 

24 

25 for key, value in inst.__dict__.items(): 

26 cls = value.__class__ 

27 if is_custom_class(cls): 

28 value = asdict(value, _level + 1) 

29 elif isinstance(value, list): 

30 value = [asdict(_, _level + 1) if instance_of_custom_class(_) else _ for _ in value] 

31 elif isinstance(value, dict): 

32 value = {k: asdict(v, _level + 1) if instance_of_custom_class(v) else v for k, v in value.items()} 

33 

34 data[key] = value 

35 

36 if _level == 0 and with_top_level_key: 

37 # top-level: add an extra key indicating the class' name 

38 cls_name = camel_to_snake(inst.__class__.__name__) 

39 return {cls_name: data} 

40 

41 return data 

42 

43 

44@register_dumper("toml") 

45def astoml(inst: typing.Any, multiline_strings: bool = False, **kw: typing.Any) -> str: 

46 """ 

47 Dump a config instance to toml (recursively). 

48 """ 

49 data = asdict(inst, with_top_level_key=kw.pop("with_top_level_key", True)) 

50 return tomli_w.dumps(data, multiline_strings=multiline_strings) 

51 

52 

53@register_dumper("json") 

54def asjson(inst: typing.Any, **kw: typing.Any) -> str: 

55 """ 

56 Dump a config instance to json (recursively). 

57 """ 

58 data = asdict(inst, with_top_level_key=kw.pop("with_top_level_key", True)) 

59 return json.dumps(data, **kw) 

60 

61 

62@register_dumper("yaml") 

63def asyaml(inst: typing.Any, **kw: typing.Any) -> str: 

64 """ 

65 Dump a config instance to yaml (recursively). 

66 """ 

67 data = asdict(inst, with_top_level_key=kw.pop("with_top_level_key", True)) 

68 output = yaml.dump(data, encoding=None, **kw) 

69 # output is already a str but mypy doesn't know that 

70 return typing.cast(str, output) 

71 

72 

73@register_dumper("bytes") 

74def asbytes(inst: "BinaryConfig", **_: typing.Any) -> bytes: 

75 """ 

76 Dumper for binary config to 'pack' into a bytestring. 

77 """ 

78 return inst._pack()