# Portable contract
Struct.to_dict() and Signature.to_dict() return a JSON-compatible schema tree.
The caller may serialize it with json.dumps. Every call returns fresh dicts and
lists. Equal definitions produce the same document across processes and hash
seeds, assuming deterministic defaults and the same interpreter configuration
(python -OO removes function docstrings).
# Document and definitions
{
"v": 1,
"kind": "struct",
"root": "User",
"defs": {
"structs": {
"User": {
"name": "User",
"fields": [{"name": "name", "shape": [{"type": "str"}]}]
}
},
"enums": {}
}
}
A signature replaces root with name, optional function doc, and params
(an array of fields); its kind is "signature". Both definition tables are
always present. Enum definitions contain name and members, an ordered array
of canonical member names; aliases and member values are omitted.
Struct and enum nodes always use ref into their respective definition tables.
Definitions are stored once, so recursive schemas terminate. IDs are local handles
assigned on first encounter: User, User#2, etc. Their name is the class name
used in data discriminators; consumers must not substitute the definition ID.
v versions the format, not the library. Removing a key or changing its meaning
raises v; additive keys do not. Ignore unknown keys and reject unsupported shape
types. Format version 1 includes tuple nodes; consumers need tuple support to
read them.
# Fields and nodes
A field has name and shape (a nonempty ordered array of option nodes).
Optional keys are label, description, optional_toggle and default.
No default means the key is absent; a None default is "default": null.
There are no optional, required or nullable keys: inspect default and
{"type": "none"} separately.
Each node has type. In a slot containing several options, each also has id,
its Shape.option_id(). IDs are unique within the dataclass namespace and within
the other-option namespace, not across both. Preserve option positions; use IDs
for discriminators. See option identities.
type |
Additional keys when applicable |
|---|---|
int |
min, max, exclusive_min, exclusive_max, multiple_of, choices, step, slider, placeholder, extras |
float |
min, max, exclusive_min, exclusive_max, choices, step, slider, placeholder, extras |
str |
min_length, max_length, pattern, pattern_message, choices, file_hint, is_password, rows, placeholder, extras |
date, time |
min, max, exclusive_min, exclusive_max, choices, placeholder, extras |
bool, none |
extras |
list |
item, min_items, max_items, extras |
tuple |
items or item, min_items, max_items, extras |
enum |
ref, extras |
struct |
ref |
item is an option array for repeated elements. items is an array of option
arrays for fixed tuple positions; items: [] describes tuple[()]. They never
coexist. A tuple with item is variadic.
{"type": "tuple", "items": [[{"type": "int"}], [{"type": "str"}]]}
slider is {"show_value": true} or {"show_value": false}.
file_hint may contain extensions (array), min_size and max_size (bytes).
extras is a dictionary of string pairs sorted by key, omitted when empty.
pattern is a Python re.fullmatch expression; consumers using other regex
engines must check compatibility.
Absent atoms and default-valued flags are omitted, including false exclusive
bounds. Exceptions preserving explicit notation are optional_toggle: false,
slider: {"show_value": false} and file_hint: {}. Dates use YYYY-MM-DD;
times use HH:MM:SS. Float atom numbers normalize to floats when exactly
representable; integer bounds with no equal float remain integers. Atom signed
zero normalizes to positive zero; a default's signed zero is preserved.
# Values and defaults
Defaults use decode's portable spelling: dates and times as strings,
enums by member name, lists and tuples as arrays, dataclasses as objects.
Factories contribute their certified product, not executable recipes.
A field default taken from the document round-trips through
schema.build(schema.decode(data)), subject to the author's
default purity requirements.
To select an option when writing portable values:
| Slot | Encoding |
|---|---|
| Multiple dataclasses | Object with inline "$type": <class name> |
| Other options sharing a portable spelling | {"$type": <option id>, "$value": <portable value>} |
| Unambiguous option | Bare portable value |
Numbers share a spelling between int and float; strings between str,
date, time and enums; arrays between lists and tuples. The rule applies to
fields and sequence slots. Exported defaults include the needed discriminators:
{"name": "limit", "default": {"$type": "float", "$value": 3.0},
"shape": [{"type": "int", "id": "int"}, {"type": "float", "id": "float"}]}
The document contains declared constraints and notation, not rendering decisions, Python objects, recipes, library versions or runtime provenance. File size constraints are declared here and enforced by the consumer holding the file.