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

1"""

2 Method to dump classes to other formats.

3"""

4

5 import json

6 import typing

7

8 import tomli_w

9 import yaml

10

11 from .helpers import camel_to_snake, instance_of_custom_class, is_custom_class

12 from .loaders.register import register_dumper

13

14 if typing.TYPE_CHECKING: # pragma: no cover

15 from .binary_config import BinaryConfig

16

17

18@register_dumper("dict")

19 def 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")

45 def 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")

54 def 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")

63 def 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")

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

75 """

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

77 """

78 return inst._pack()