"""The purpose of this module is to "collapse" the intermediate representation of a TOML
document into Python builtin data types (mainly a composition of :class:`dict`,
:class:`list`, :class:`int`, :class:`float`, :class:`bool`, :class:`str`).
This is **not a loss-less** process, since comments are not preserved.
"""
from collections.abc import Mapping, MutableMapping
from functools import singledispatch
from typing import Any, TypeVar
from ..errors import InvalidTOMLKey
from ..types import Commented, CommentedKV, CommentedList, HiddenKey, IntermediateRepr
__all__ = [
"convert",
"collapse",
]
M = TypeVar("M", bound=MutableMapping)
[docs]def convert(irepr: IntermediateRepr) -> dict:
return collapse(irepr)
[docs]@singledispatch
def collapse(obj):
# Catch all
return obj
@collapse.register(Commented)
def _collapse_commented(obj: Commented) -> Any:
return obj.value_or(None)
@collapse.register(CommentedList)
def _collapse_commented_list(obj: CommentedList) -> list:
return [collapse(v) for v in obj.as_list()]
@collapse.register(CommentedKV)
def _collapse_commented_kv(obj: CommentedKV) -> dict:
return {k: collapse(v) for k, v in obj.as_dict().items()}
@collapse.register(Mapping)
def _collapse_mapping(obj: Mapping) -> dict:
return _convert_irepr_to_dict(obj, {})
@collapse.register(list)
def _collapse_list(obj: list) -> list:
return [collapse(e) for e in obj]
def _convert_irepr_to_dict(irepr: Mapping, out: M) -> M:
for key, value in irepr.items():
if isinstance(key, HiddenKey):
continue
elif isinstance(key, tuple):
parent_key, *rest = key
if not isinstance(parent_key, str):
raise InvalidTOMLKey(key)
p = out.setdefault(parent_key, {})
if not isinstance(p, MutableMapping):
msg = f"Value for `{parent_key}` expected to be Mapping, found {p!r}"
raise ValueError(msg)
nested_key = rest[0] if len(rest) == 1 else tuple(rest)
_convert_irepr_to_dict({nested_key: value}, p)
elif isinstance(key, (int, str)):
out[str(key)] = collapse(value)
return out