Skip to content

JSON Schema export for DataSet classes #102

@PierreRaybaut

Description

@PierreRaybaut

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

{
  "$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-layout key 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 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions