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 .core import instance_of_custom_class, is_custom_class

12 from .helpers import camel_to_snake

13

14

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

16 """

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

18 """

19 data = {}

20

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

22 cls = value.__class__

23 if is_custom_class(cls):

24 value = asdict(value, _level + 1)

25 elif isinstance(value, list):

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

27 elif isinstance(value, dict):

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

29

30 data[key] = value

31

32 if _level == 0 and with_top_level_key:

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

34 cls_name = camel_to_snake(inst.__class__.__name__)

35 return {cls_name: data}

36

37 return data

38

39

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

41 """

42 Dump a config instance to toml (recursively).

43 """

44 data = asdict(inst)

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

46

47

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

49 """

50 Dump a config instance to json (recursively).

51 """

52 data = asdict(inst)

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

54

55

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

57 """

58 Dump a config instance to yaml (recursively).

59 """

60 data = asdict(inst)

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

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

63 return typing.cast(str, output)