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
|