Skip to content

The Schema model & OSD

For the formal Schema model and the full OSD grammar, see spec.omnist.dev's Schema Model chapter and its OSD Grammar chapter. This page covers only how OSD and the Schema model map to this library's Python API.

from omnist import parse_schema, doc

s = parse_schema('''
    record Database { "type": string, "server": string, "port": integer }
    record Service  { "host": string, "port": integer,
                       "databases" [1,]: Database, "tags" [0,]: string }
    root Service
''')
s.validate(doc({"host": "api.internal", "port": 8443,
                "databases": [{"type": "prod", "server": "db1.internal.example.com", "port": 5432}],
                "tags": ["prod", "us-east"]})).ok    # True

Scalar kinds and their Python types

A Scalar's kind determines which Python type a conforming value is held as. Validation only checks; deserialization (materialize, or schema= on a reader) additionally converts, succeeding only when the conversion is value-exact, and raising ParseError otherwise.

Scalar kind Canonical Python type What deserialization additionally converts What it rejects
string str nothing every non-str value
integer int a float with no fractional part (4.0 → 4) bool; a float with a fractional part; any str
number float an int is always upgraded to float (3 → 3.0) bool; any str
boolean bool nothing (no "true"/"false" string parsing) every non-bool value
date datetime.date an ISO-8601 date string, to a real date a real datetime (even though it's a date subclass); an invalid date string
time datetime.time an ISO-8601 time string, to a real time an invalid time string
datetime datetime.datetime a full ISO-8601 timestamp string, to a real datetime a bare ISO date string (satisfies only date); an invalid timestamp string

bool never satisfies integer/number even though Python's bool is an int subclass, and datetime never satisfies date even though it's a date subclass — both scalars stay mutually exclusive on purpose. See Schema-directed deserialization for the full conversion pipeline.

The Python builder

The same schema, built from Python instead of parsed from text. Scalar instances live under the t namespace (and also as top-level names STRING, INTEGER, …) and are passed as-is as a field's type:

from omnist import schema, record, field, ref, nullable, t

address = record(field("street", t.string), field("city", t.string))
user = record(
    field("name",     t.string),
    field("nickname", t.string, min=0, max=1),
    field("emails",   t.string, min=1, max=None),     # [1,]
    field("address",  ref("Address")),
    field("note",     nullable(t.string)),            # nullable scalar
)
s2 = schema(ref("User"), User=user, Address=address)

s.equivalent(s2)      # True -- same schema, built two different ways

t.string / t.integer / t.number / t.boolean / t.date / t.time / t.datetime are ready-to-use Scalar instances; nullable(scalar) returns a nullable copy; field(label, type, min=1, max=1); record(*fields); schema(root_ref, **named_definitions). to_osd(schema) serializes a Schema built either way back to OSD text, and to_osd(schema, indent=None) renders it on a single line instead:

from omnist import to_osd

to_osd(parse_schema('record Car { "license": string }\nroot Car'))
# 'record Car {\n    "license": string,\n}\nroot Car\n'
to_osd(parse_schema('record Car { "license": string }\nroot Car'), indent=None)
# 'record Car { "license": string } root Car\n'

Both forms round-trip through parse_schema to an equivalent Schema. OSD comments (# to end of line) are lexical trivia and never round-trip through to_osd().

Schema operations, by Python call

Each schema-algebra operation (spec.omnist.dev ch. 6) is a method on Schema (or, for infer, a top-level function):

Operation Python call
Validate a document schema.validate(doc) → ValidationResult(.ok, .errors)
Compatibility / equivalence schema.compatible_with(other), schema.equivalent(other)
Canonical minimal form schema.normalize()
Drop unreachable/unsatisfiable structure schema.prune()
Subschema by label set schema.extract(*labels) — raises SchemaError if dropping a label would delete a mandatory field
Draft a schema from samples infer(samples) (top-level function)
Structural lint schema.lint() → list of LintFinding(code, severity, location, message); see the CLI reference

See the guide for a worked walkthrough of all of these, including infer's exact cardinality/nullability rules.

See also

  • User guide — the practical tour, including the Python builder, validation, operations, and inference in full.
  • OML — the native format for the Documents a schema validates.
  • A real-life example — one order schema validated against an OML document, plus a backward-compatibility check.
  • Schema Model chapter and OSD Grammar chapter on spec.omnist.dev — the formal definitions.