Summary
Add a new module guidata.dataset.jsonschema that exposes a UI-framework-agnostic representation of any guidata.dataset.DataSet subclass as a JSON Schema 2020-12 document, augmented with x-guidata-* extension keywords for the guidata-specific concerns that JSON Schema cannot natively express (groups, tabs, units, choice labels, dynamic choices, image icons, …).
It is the non-Qt equivalent of guidata.dataset.qtwidgets: it describes the form to be rendered, leaving the actual rendering to any frontend (web, CLI, notebook, …).
Motivation
guidata is currently tied to Qt for parameter editing. Several downstream projects need to render the same parameter sets in non-Qt environments:
- DataLab-Web (browser-native sibling of DataLab) renders Sigima parameter dialogs in React + TypeScript inside Pyodide. It currently has to maintain a hand-written shim that reflects on
DataSet internals to produce a JSON description for the JS layer — fragile and out of sync with new item kinds.
- DataLab-Kernel (Jupyter kernel) needs the same description to render parameter forms in notebooks (ipywidgets) without importing Qt.
- Any third-party HTTP/JSON service exposing a Sigima/DataLab computation would benefit from a stable, versioned schema.
A standardized JSON Schema export removes the need for each frontend to re-implement reflection on DataSet classes, and provides a stable contract for cross-process and cross-language consumption.
Public API
Three functions are added under guidata.dataset (re-exported from the package __init__):
from guidata.dataset import (
dataset_to_schema,
dataset_to_schema_with_values,
resolve_dynamic_choices,
)
dataset_to_schema(dataset_cls) -> dict
Returns a JSON-serialisable dict that conforms to JSON Schema 2020-12 and carries guidata-specific information under x-guidata-* keys.
dataset_to_schema_with_values(instance) -> dict
Returns {"schema": ..., "values": ...} where values is a JSON-serialisable mapping of property name → current value of the instance (suitable for hydrating a frontend form).
resolve_dynamic_choices(instance, item_name) -> list[dict]
Resolves a dynamic ChoiceItem (one whose choices is a callable) against the current state of a DataSet instance. The frontend calls this whenever any value the callable depends on changes.
Output format
Layout tree
The x-guidata-layout key is a tree of nodes describing the visual grouping (groups, tab groups, tabs, separators) that JSON Schema cannot natively express:
Leaf entries are property names (strings) that reference top-level properties keys. This preserves both the flat schema (easy to validate) and the nested layout (needed for rendering).
Item kinds
Each property carries an x-guidata-kind keyword identifying the intended widget. Kinds emitted by this module:
int, float, bool, string, text, choice, multiple_choice, image_choice, color, date, datetime, file, float_array, dict.
Item-specific extensions
Examples of the x-guidata-* keywords emitted for items beyond what JSON Schema covers:
| Keyword |
Purpose |
x-guidata-unit |
Physical unit (numeric items) |
x-guidata-step |
UI step for floats (looser than multipleOf) |
x-guidata-slider |
Render numeric input as a slider |
x-guidata-nonzero |
Reject zero values |
x-guidata-text |
Inline text next to a checkbox |
x-guidata-password |
Mask string item input |
x-guidata-wordwrap |
Word-wrap a string item |
x-guidata-choices |
Ordered [{value, label}] list (with enum) |
x-guidata-choices-dynamic |
Choices come from a runtime callable |
x-guidata-file-mode |
open / open-multi / save / directory |
x-guidata-file-filters |
File-dialog filters |
x-guidata-file-basedir |
Default base directory |
x-guidata-format |
Display format (e.g. "%.3f" for float arrays) |
x-guidata-transpose |
Display arrays transposed |
x-guidata-py-format |
Python strftime pattern for date/datetime |
icon (in choice entries) |
data: URL of ImageChoiceItem icons |
Versioning
The schema carries "x-guidata-version": 1. The constant SCHEMA_VERSION is defined at module scope and bumped on breaking changes so frontends can refuse to consume newer schemas.
Out of scope / explicit limitations
ButtonItem raises NotImplementedError (callbacks cannot cross JSON). Frontends that need actions must wire them outside the schema.
- Conditional visibility through callable
active props is not exported. Static booleans could be added in a follow-up.
- Unknown / advanced item kinds emit a placeholder
{"x-guidata-kind": "unknown", "x-guidata-class": "..."} so the rest of the schema remains usable; frontends should fall back to a read-only display.
Tests
A test module guidata/tests/dataset/test_jsonschema.py covers:
- numeric / bool / string / text item conversion (including bounds, units, regexp, password, wordwrap, slider, nonzero);
- single and multiple choice items, including dynamic choices and resolution via
resolve_dynamic_choices;
ImageChoiceItem icon inlining as data: URLs;
- file / directory / multi-file items;
- date / datetime items;
- float array and dict items;
- group / tab-group / tab / separator layout reconstruction;
dataset_to_schema_with_values round-trip;
ButtonItem rejection.
Backward compatibility
Pure addition. No public API of guidata is changed; only three new symbols are exported from guidata.dataset. No new mandatory dependencies (uses only the standard library plus the existing guidata.dataset internals).
Files added / modified
- Added:
guidata/dataset/jsonschema.py
- Added:
guidata/tests/dataset/test_jsonschema.py
- Modified:
guidata/dataset/__init__.py (re-export the three public functions)
Possible follow-ups (not in this proposal)
- Static export of conditional visibility (
active boolean props).
- A small JS / TS reference renderer in the documentation.
- A
schema_to_dataset() inverse helper for round-tripping schemas authored outside Python.
Summary
Add a new module
guidata.dataset.jsonschemathat exposes a UI-framework-agnostic representation of anyguidata.dataset.DataSetsubclass as a JSON Schema 2020-12 document, augmented withx-guidata-*extension keywords for the guidata-specific concerns that JSON Schema cannot natively express (groups, tabs, units, choice labels, dynamic choices, image icons, …).It is the non-Qt equivalent of
guidata.dataset.qtwidgets: it describes the form to be rendered, leaving the actual rendering to any frontend (web, CLI, notebook, …).Motivation
guidatais currently tied to Qt for parameter editing. Several downstream projects need to render the same parameter sets in non-Qt environments:DataSetinternals to produce a JSON description for the JS layer — fragile and out of sync with new item kinds.A standardized JSON Schema export removes the need for each frontend to re-implement reflection on
DataSetclasses, and provides a stable contract for cross-process and cross-language consumption.Public API
Three functions are added under
guidata.dataset(re-exported from the package__init__):dataset_to_schema(dataset_cls) -> dictReturns a JSON-serialisable dict that conforms to JSON Schema 2020-12 and carries guidata-specific information under
x-guidata-*keys.dataset_to_schema_with_values(instance) -> dictReturns
{"schema": ..., "values": ...}wherevaluesis a JSON-serialisable mapping of property name → current value of the instance (suitable for hydrating a frontend form).resolve_dynamic_choices(instance, item_name) -> list[dict]Resolves a dynamic
ChoiceItem(one whosechoicesis a callable) against the current state of aDataSetinstance. The frontend calls this whenever any value the callable depends on changes.Output format
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "urn:guidata:dataset:my.module.MyParam", "title": "My parameters", "description": "Class docstring (excluding the title line).", "type": "object", "properties": { "n": { "type": "integer", "minimum": 1, "default": 5, "description": "Window size", "x-guidata-kind": "int", "x-guidata-order": 0 } // ... }, "required": ["n"], "x-guidata-version": 1, "x-guidata-property-order": ["n", /* ... */], "x-guidata-layout": [ /* layout tree */ ] }Layout tree
The
x-guidata-layoutkey is a tree of nodes describing the visual grouping (groups, tab groups, tabs, separators) that JSON Schema cannot natively express:{ "kind": "group" | "tab-group" | "tab" | "separator", "label": "...", "items": [ /* child node or property name */ ] }Leaf entries are property names (strings) that reference top-level
propertieskeys. This preserves both the flat schema (easy to validate) and the nested layout (needed for rendering).Item kinds
Each property carries an
x-guidata-kindkeyword identifying the intended widget. Kinds emitted by this module:int,float,bool,string,text,choice,multiple_choice,image_choice,color,date,datetime,file,float_array,dict.Item-specific extensions
Examples of the
x-guidata-*keywords emitted for items beyond what JSON Schema covers:x-guidata-unitx-guidata-stepmultipleOf)x-guidata-sliderx-guidata-nonzerox-guidata-textx-guidata-passwordx-guidata-wordwrapx-guidata-choices[{value, label}]list (withenum)x-guidata-choices-dynamicx-guidata-file-modeopen/open-multi/save/directoryx-guidata-file-filtersx-guidata-file-basedirx-guidata-format"%.3f"for float arrays)x-guidata-transposex-guidata-py-formatstrftimepattern for date/datetimeicon(in choice entries)data:URL ofImageChoiceItemiconsVersioning
The schema carries
"x-guidata-version": 1. The constantSCHEMA_VERSIONis defined at module scope and bumped on breaking changes so frontends can refuse to consume newer schemas.Out of scope / explicit limitations
ButtonItemraisesNotImplementedError(callbacks cannot cross JSON). Frontends that need actions must wire them outside the schema.activeprops is not exported. Static booleans could be added in a follow-up.{"x-guidata-kind": "unknown", "x-guidata-class": "..."}so the rest of the schema remains usable; frontends should fall back to a read-only display.Tests
A test module
guidata/tests/dataset/test_jsonschema.pycovers:resolve_dynamic_choices;ImageChoiceItemicon inlining asdata:URLs;dataset_to_schema_with_valuesround-trip;ButtonItemrejection.Backward compatibility
Pure addition. No public API of
guidatais changed; only three new symbols are exported fromguidata.dataset. No new mandatory dependencies (uses only the standard library plus the existingguidata.datasetinternals).Files added / modified
guidata/dataset/jsonschema.pyguidata/tests/dataset/test_jsonschema.pyguidata/dataset/__init__.py(re-export the three public functions)Possible follow-ups (not in this proposal)
activeboolean props).schema_to_dataset()inverse helper for round-tripping schemas authored outside Python.