Skip to content

Field lists

Some tools do not want a schema document, they want a flat list of fields to render as a form. to_field_list(schema) produces exactly that. It is exported from the top level: from probatio import to_field_list.

Unlike JSON Schema and OpenAPI, this is not a published standard. It is the shape voluptuous-serialize emits, an internal format from the Home Assistant ecosystem, where the config-flow frontend turns a schema into a form. Probatio matches it byte for byte so anything built against voluptuous-serialize keeps working on a Probatio schema. That is who this codec is for: Home Assistant and the libraries around it. If you are not in that world, reach for JSON Schema or OpenAPI instead.

A mapping becomes a list of field dicts, one per key, carrying the type, the name, and whether it is required:

from probatio import Schema, Required, Optional, In, to_field_list
schema = Schema(
{
Required("name"): str,
Optional("port", default=8080): int,
Required("mode"): In(["auto", "manual"]),
}
)
to_field_list(schema)
# [{'type': 'string', 'name': 'name', 'required': True}, {'type': 'integer', 'name': 'port', 'required': False, 'optional': True, 'default': 8080}, {'type': 'select', 'options': [('auto', 'auto'), ('manual', 'manual')], 'name': 'mode', 'required': True}]

Each field carries what the frontend needs to render it: the type, the name, required, an optional flag and a default when present, and bounds (such as valueMin/valueMax for a Range) where the validator implies them. An In(...) becomes a select field with its options as (value, label) pairs, which is how a config-flow form renders a dropdown; pass In a mapping to give each value its own label.

to_field_list and to_openapi take a custom_serializer hook, called first for each node. It returns a dict to override that node, or the UNSUPPORTED sentinel to defer to the default handling. UNSUPPORTED is exported from the top level and prints as itself:

from probatio import UNSUPPORTED
UNSUPPORTED # UNSUPPORTED

The hook is a plain function taking one schema node. Return a dict to render that node yourself; return UNSUPPORTED for everything else. Here a Port renders as a dedicated port field type instead of the default bounded integer:

from probatio import Schema, Required, Port, to_field_list, UNSUPPORTED
def render_port(node):
if isinstance(node, Port):
return {"type": "port"}
return UNSUPPORTED
schema = Schema({Required("host"): str, Required("port"): Port()})
to_field_list(schema, custom_serializer=render_port)
# [{'type': 'string', 'name': 'host', 'required': True}, {'type': 'port', 'name': 'port', 'required': True}]

The hook overrides the value part only; to_field_list still adds the key facets (name, required, default) around whatever the hook returns.