Skip to content

struct

struct #

Struct — named fields. Subclass Struct to define one.

class Session(cg.Struct):
    photo: cg.Image
    location: cg.Location

There is no factory and no decorator: __init_subclass__ reads the annotations into _fields and turns the class into a dataclass, so the __init__ / __repr__ / __eq__ over the fields come for free, and the class you wrote is the type. from_schema builds one with a plain type(...) call (it triggers the same hook).

On disk, a field whose name starts with _ is hidden: the leading _ becomes a leading . (_metadata lives at .metadata). That mapping is applied only at the filesystem boundary; the schema keeps the logical name.

Struct #

Bases: CGType

A type with named fields. Subclass it; the annotations are the fields.

The dataclass_transform marker lets type checkers synthesize each subclass's __init__ from its annotated fields — the runtime turns the class into a dataclass in __init_subclass__ below.

Source code in capturegraph-lib/capturegraph/types/containers/struct.py
@dataclass_transform()
class Struct(CGType):
    """A type with named fields. Subclass it; the annotations are the fields.

    The ``dataclass_transform`` marker lets type checkers synthesize each
    subclass's ``__init__`` from its annotated fields — the runtime turns the
    class into a dataclass in ``__init_subclass__`` below.
    """

    _fields: ClassVar[dict[str, type[CGType]]] = {}

    def __init_subclass__(cls, **kwargs: object) -> None:
        """Turn ``cls`` into a dataclass over its annotated CGType fields."""
        super().__init_subclass__(**kwargs)
        # ``eval_str`` resolves string annotations (``from __future__ import
        # annotations``) back to the real CGType classes the fields name.
        annotations = inspect.get_annotations(cls, eval_str=True)
        if not annotations:
            return
        for name, field_type in annotations.items():
            check_element(field_type, f"Struct field {name!r}")
        cls._fields = annotations
        dataclass(cls)  # in place: gives __init__ / __repr__ / __eq__ over the fields

    @classmethod
    def schema(cls) -> dict[str, JSONValue]:
        """This struct's wire form: each field's schema, tagged ``"struct"``."""
        return {
            "type": "struct",
            "fields": {n: t.schema() for n, t in cls._fields.items()},
        }

    @classmethod
    def load(cls, base: Path) -> Self:
        """Read each field under ``base`` (dotted for a hidden name) into a value."""
        values: dict[str, object] = {}
        for name, field in cls._fields.items():
            try:
                values[name] = field.load(base / on_disk(name))
            except FileNotFoundError as error:
                values[name] = _Missing(error)  # absence is a filesystem fact
        return cls(**values)

    @classmethod
    def store(cls, base: Path, value: object) -> None:
        """Write each present field of ``value`` (an instance) under ``base``."""
        if not isinstance(value, cls):
            raise TypeError(
                f"{cls.__name__}.store needs a {cls.__name__}, got {value!r}",
            )
        base.mkdir(parents=True, exist_ok=True)
        for name, field in cls._fields.items():
            child = getattr(value, name)
            if is_missing(child):
                continue  # absence is a filesystem fact: an absent field stays absent
            field.store(base / on_disk(name), child)

__init_subclass__(**kwargs) #

Turn cls into a dataclass over its annotated CGType fields.

Source code in capturegraph-lib/capturegraph/types/containers/struct.py
def __init_subclass__(cls, **kwargs: object) -> None:
    """Turn ``cls`` into a dataclass over its annotated CGType fields."""
    super().__init_subclass__(**kwargs)
    # ``eval_str`` resolves string annotations (``from __future__ import
    # annotations``) back to the real CGType classes the fields name.
    annotations = inspect.get_annotations(cls, eval_str=True)
    if not annotations:
        return
    for name, field_type in annotations.items():
        check_element(field_type, f"Struct field {name!r}")
    cls._fields = annotations
    dataclass(cls)  # in place: gives __init__ / __repr__ / __eq__ over the fields

load(base) classmethod #

Read each field under base (dotted for a hidden name) into a value.

Source code in capturegraph-lib/capturegraph/types/containers/struct.py
@classmethod
def load(cls, base: Path) -> Self:
    """Read each field under ``base`` (dotted for a hidden name) into a value."""
    values: dict[str, object] = {}
    for name, field in cls._fields.items():
        try:
            values[name] = field.load(base / on_disk(name))
        except FileNotFoundError as error:
            values[name] = _Missing(error)  # absence is a filesystem fact
    return cls(**values)

schema() classmethod #

This struct's wire form: each field's schema, tagged "struct".

Source code in capturegraph-lib/capturegraph/types/containers/struct.py
@classmethod
def schema(cls) -> dict[str, JSONValue]:
    """This struct's wire form: each field's schema, tagged ``"struct"``."""
    return {
        "type": "struct",
        "fields": {n: t.schema() for n, t in cls._fields.items()},
    }

store(base, value) classmethod #

Write each present field of value (an instance) under base.

Source code in capturegraph-lib/capturegraph/types/containers/struct.py
@classmethod
def store(cls, base: Path, value: object) -> None:
    """Write each present field of ``value`` (an instance) under ``base``."""
    if not isinstance(value, cls):
        raise TypeError(
            f"{cls.__name__}.store needs a {cls.__name__}, got {value!r}",
        )
    base.mkdir(parents=True, exist_ok=True)
    for name, field in cls._fields.items():
        child = getattr(value, name)
        if is_missing(child):
            continue  # absence is a filesystem fact: an absent field stays absent
        field.store(base / on_disk(name), child)

on_disk(field_name) #

The on-disk entry name for a struct field (_metadata.metadata).

Source code in capturegraph-lib/capturegraph/types/containers/struct.py
def on_disk(field_name: str) -> str:
    """The on-disk entry name for a struct field (``_metadata`` → ``.metadata``)."""
    return f".{field_name[1:]}" if field_name.startswith("_") else field_name