turbo_broccoli.custom.embedded

Embedded JSON documents.

Serializing a EmbeddedDict or a EmbeddedList will (unconditionally) result in its own JSON artefact being created and referenced by the main JSON document.

  1"""
  2Embedded JSON documents.
  3
  4Serializing a `EmbeddedDict` or a `EmbeddedList` will (unconditionally) result
  5in its own JSON artefact being created and referenced by the main JSON
  6document.
  7"""
  8
  9from pathlib import Path
 10from typing import Any, Callable, Tuple
 11
 12from ..context import Context
 13from ..exceptions import DeserializationError, TypeNotSupported
 14
 15
 16class EmbeddedDict(dict):
 17    """See module documentation"""
 18
 19    _tb_artifact_id: str | None = None
 20
 21
 22class EmbeddedList(list):
 23    """See module documentation"""
 24
 25    _tb_artifact_id: str | None = None
 26
 27
 28def _get_artifact_path(
 29    obj: EmbeddedDict | EmbeddedList, ctx: Context
 30) -> tuple[Path, str]:
 31    """
 32    If the object has an `_tb_artifact_id`, return the corresponding path and
 33    id. Otherwise, generate new ones.
 34
 35    Warning:
 36        Does not NOT set the `_tb_artifact_id` attribute.
 37    """
 38    if obj._tb_artifact_id is None:
 39        return ctx.new_artifact_path(extension="json")
 40    name = obj._tb_artifact_id
 41    path = ctx.id_to_artifact_path(name, extension="json")
 42    return path, name
 43
 44
 45def _embedded_dict_to_json(obj: EmbeddedDict, ctx: Context) -> dict:
 46    from turbo_broccoli.turbo_broccoli import to_json as _to_json
 47
 48    path, name = _get_artifact_path(obj, ctx)
 49    with path.open("w", encoding="utf-8") as fp:
 50        fp.write(_to_json(dict(obj), ctx))
 51    obj._tb_artifact_id = name
 52    return {"__type__": "embedded.dict", "__version__": 1, "id": name}
 53
 54
 55# TODO: deduplicate with _embedded_dict_to_json
 56def _embedded_list_to_json(obj: EmbeddedList, ctx: Context) -> dict:
 57    from turbo_broccoli.turbo_broccoli import to_json as _to_json
 58
 59    path, name = _get_artifact_path(obj, ctx)
 60    with path.open("w", encoding="utf-8") as fp:
 61        fp.write(_to_json(list(obj), ctx))
 62    obj._tb_artifact_id = name
 63    return {"__type__": "embedded.list", "__version__": 1, "id": name}
 64
 65
 66def _json_to_embedded_dict(dct: dict, ctx: Context) -> EmbeddedDict:
 67    decoders = {
 68        1: _json_to_embedded_dict_v1,
 69    }
 70    return decoders[dct["__version__"]](dct, ctx)
 71
 72
 73def _json_to_embedded_dict_v1(dct: dict, ctx: Context) -> EmbeddedDict:
 74    from turbo_broccoli.turbo_broccoli import from_json as _from_json
 75
 76    path = ctx.id_to_artifact_path(dct["id"], extension="json")
 77    with path.open("r", encoding="utf-8") as fp:
 78        obj = EmbeddedDict(_from_json(fp.read(), ctx))
 79    obj._tb_artifact_id = dct["id"]
 80    return obj
 81
 82
 83def _json_to_embedded_list(dct: dict, ctx: Context) -> EmbeddedList:
 84    decoders = {
 85        1: _json_to_embedded_list_v1,
 86    }
 87    return decoders[dct["__version__"]](dct, ctx)
 88
 89
 90# TODO: deduplicate with _json_to_embedded_dict_v1
 91def _json_to_embedded_list_v1(dct: dict, ctx: Context) -> EmbeddedList:
 92    from turbo_broccoli.turbo_broccoli import from_json as _from_json
 93
 94    path = ctx.id_to_artifact_path(dct["id"], extension="json")
 95    with path.open("r", encoding="utf-8") as fp:
 96        obj = EmbeddedList(_from_json(fp.read(), ctx))
 97    obj._tb_artifact_id = dct["id"]
 98    return obj
 99
100
101def from_json(dct: dict, ctx: Context) -> Any:
102    decoders = {
103        "embedded.dict": _json_to_embedded_dict,
104        "embedded.list": _json_to_embedded_list,
105    }
106    try:
107        type_name = dct["__type__"]
108        return decoders[type_name](dct, ctx)
109    except KeyError as exc:
110        raise DeserializationError() from exc
111
112
113def to_json(obj: Any, ctx: Context) -> dict:
114    """
115    Serializes a `EmbeddedDict` or an `EmbeddedList` into JSON. The return dict
116    has the following structure
117
118    ```py
119    {
120        "__type__": "embedded.dict",
121        "__version__": 1,
122        "id": <uuid4>,
123    }
124    ```
125
126    or
127
128    ```py
129    {
130        "__type__": "embedded.list",
131        "__version__": 1,
132        "id": <uuid4>,
133    }
134    ```
135
136    where the UUID points to the artefact containing the actual data.
137    """
138    encoders: list[Tuple[type, Callable[[Any, Context], dict]]] = [
139        (EmbeddedDict, _embedded_dict_to_json),
140        (EmbeddedList, _embedded_list_to_json),
141    ]
142    for t, f in encoders:
143        if isinstance(obj, t):
144            return f(obj, ctx)
145    raise TypeNotSupported()
class EmbeddedDict(builtins.dict):
17class EmbeddedDict(dict):
18    """See module documentation"""
19
20    _tb_artifact_id: str | None = None

See module documentation

Inherited Members
builtins.dict
get
setdefault
pop
popitem
keys
items
values
update
fromkeys
clear
copy
class EmbeddedList(builtins.list):
23class EmbeddedList(list):
24    """See module documentation"""
25
26    _tb_artifact_id: str | None = None

See module documentation

Inherited Members
builtins.list
list
clear
copy
append
insert
extend
pop
remove
index
count
reverse
sort
def from_json(dct: dict, ctx: turbo_broccoli.context.Context) -> Any:
102def from_json(dct: dict, ctx: Context) -> Any:
103    decoders = {
104        "embedded.dict": _json_to_embedded_dict,
105        "embedded.list": _json_to_embedded_list,
106    }
107    try:
108        type_name = dct["__type__"]
109        return decoders[type_name](dct, ctx)
110    except KeyError as exc:
111        raise DeserializationError() from exc
def to_json(obj: Any, ctx: turbo_broccoli.context.Context) -> dict:
114def to_json(obj: Any, ctx: Context) -> dict:
115    """
116    Serializes a `EmbeddedDict` or an `EmbeddedList` into JSON. The return dict
117    has the following structure
118
119    ```py
120    {
121        "__type__": "embedded.dict",
122        "__version__": 1,
123        "id": <uuid4>,
124    }
125    ```
126
127    or
128
129    ```py
130    {
131        "__type__": "embedded.list",
132        "__version__": 1,
133        "id": <uuid4>,
134    }
135    ```
136
137    where the UUID points to the artefact containing the actual data.
138    """
139    encoders: list[Tuple[type, Callable[[Any, Context], dict]]] = [
140        (EmbeddedDict, _embedded_dict_to_json),
141        (EmbeddedList, _embedded_list_to_json),
142    ]
143    for t, f in encoders:
144        if isinstance(obj, t):
145            return f(obj, ctx)
146    raise TypeNotSupported()

Serializes a EmbeddedDict or an EmbeddedList into JSON. The return dict has the following structure

{
    "__type__": "embedded.dict",
    "__version__": 1,
    "id": <uuid4>,
}

or

{
    "__type__": "embedded.list",
    "__version__": 1,
    "id": <uuid4>,
}

where the UUID points to the artefact containing the actual data.