Mercurial > hgrepos > Python > libs > data-schema
diff docs/schema.txt @ 5:84dfd1a94926
Add the existing implementation.
All tests work.
The documentation as text file is included also.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Thu, 06 Jul 2023 23:41:41 +0200 |
| parents | |
| children | c3a0fe8d4587 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/schema.txt Thu Jul 06 23:41:41 2023 +0200 @@ -0,0 +1,542 @@ +.. -*- coding: utf-8; mode: rst; indent-tabs-mode: nil -*- + +======== + Schema +======== + +Grundideen +========== + +- Angelehnt an JSON Schema +- Deklarativ in YAML +- Verwendung von erweiterten YAML-Features: + + + laden von (beliebigen) Python-Objekten + + Benutzung von YAML-Referenzen + +- Möglichkeit der direkten Verwendung von Python-Callables + Diese müssen Iteratoren sein und jedes Problem `yield`en. +- Rückgabe einer Liste von Problemen: Versuch möglichst viele Probleme auf + einen Schlag zu melden (soweit möglich und sinnvoll) + +.. seealso:: - https://json-schema.org/understanding-json-schema/index.html + - http://rx.codesimply.com/coretypes.html + + +Extra Keywords für :py:func:`validate` +====================================== + +- ``skip_keys`` + + Eine Liste Strings oder von compilierten REs + + Ein String-Item wird auf Gleichheit getestet, die RE per :py:meth:`search` + -- und zwar auf den Dict-key + + Bei Treffer wird dieser Key komplett ignoriert. Das ist also eine globale + Ignore-Liste für Dict-Keys. + + +Typen +===== + +Durch ``type`` (required) gekennzeichnet + + +Alle Schemata außer den `Schema-Kombinatoren`_ haben auch ein optionales +Attribut ``index-constraint``. + + Dessen Wert ist eine Liste von Indizes, an denen das Element in + seinem Parent-Container (Liste, sorted dict) vorkommen darf. + + +dict / map / object +------------------- + +- ``nullable`` + + bool (Default: False): instead of an empty dict allow also a None/null/nil + +- ``keys`` + + `dict` mit Keys und den Values als zugeordnete Schemata für die Values + des Dicts + +- ``keyNames`` + + Wenn vorhanden: ein Schema, dem die *Keys* -- auch die `additionalKeys` -- + folgen müssen. + + Default: entspricht ``{"type": "string"}`` + +- ``additionalKeys`` + + * bool + + `False` + nicht erlaubt (default) + + Globales ``skip_keys`` wird aber zusätzlich noch in Betracht + gezogen. + + `True` + erlaubt -- keine weitergehende Schema-Prüfung der Inhalte + + Globales ``skip_keys`` ist offensichtlich irrelevant. + + * Schema + + Prüfung erfolgt nach gegebenem Schema + + Globales ``skip_keys`` wird aber zusätzlich noch in Betracht gezogen. + +- ``required`` + + Liste von Strings mit Key-Namen, die vorkommen müssen + +- ``maxLength`` +- ``minLength`` + + +list / array +------------ + +- ``nullable`` + + bool (Default: False): instead of an empty list allow also a None/null/nil + +- ``items`` + + Ein Schema für *alle* Items. + +- ``maxLength`` +- ``minLength`` + + +set / frozenset +--------------- + +- ``nullable`` + + bool (Default: False): instead of an empty set allow also a None/null/nil + +- ``items`` + + Ein Schema für *alle* Items + +- ``maxLength`` +- ``minLength`` + + +tuple / record +-------------- + +- ``nullable`` + + bool (Default: False): instead of an empty list or tuple allow also + a None/null/nil + +- ``items`` + + Eine Liste: je ein spezielles Schema *pro Item* + +- ``additionalItems`` + + * bool + + `False` + nicht erlaubt (default) + + `True` + erlaubt -- keine weitergehende Schema-Prüfung der Inhalte + + * Schema + + Prüfung der "zusätzlichen" Items erfolgt nach gegebenem Schema + +- ``maxLength`` +- ``minLength`` + + +string / str +------------ + +- ``nullable`` + + bool (Default: False): instead of an empty string allow also a None/null/nil + +- ``enum`` + + Eine Liste von Strings, von denen genau einer dem String entspricht + + Achtung: Alle anderen Prüfungen (siehe unten) werden trotzdem auch + durchgeführt. + +- ``is-contained-in-ref`` + + The string's value must be contained in (Python ``in``) in the referenced + object (see `Referenzen`_). + +- ``maxLength`` +- ``minLength`` +- ``pattern`` + + * string + + RE of the accepted pattern + + * compiled RE + + compiled RE of the accepted pattern + + * Callable + + +binary +------ + +- ``maxLength`` +- ``minLength`` +- ``pattern`` + + * string + + RE of the accepted pattern. The YAML unicode string value will be + converted to a byte-string with :func:`ast.literal_eval` as if it + is surrounded by ``b'''<re>'''`` or ``b"""<re>"""``. If the pattern + contains both a ``'''`` or ``"""`` substring the conversion will fail. + + * bytes, bytearray + + RE of the accepted pattern + + * compiled RE + + compiled RE of the accepted pattern + + * Callable + + +bool / boolean +-------------- + +Only **real** boolean values: ``true`` and ``false`` + +- ``value`` + + The accepted value or a validating callable + +- ``nullable`` + + bool (Default: False): instead of a boolean allow also a None/null/nil + + +timestamp / datetime +-------------------- + +Only :py:class:`datetime.datetime` allowed + +- ``value`` + + Callable that validates the value of a timestamp + + +Callable +-------- + +Iterator (e.g. ``yield``) mit Signatur: :py:func:`callable(object, schema, context)` + + +accept +------ + +Validates successfully always: accept everything + + +deny +---- + +Does not validate successfully: always yield the error code 10010 + + +:py:obj:`None` / none / null / nil +---------------------------------- + +Only the `None` object validates + + +empty +----- + +Erlaubt sind: None, leeres Dict, leere Liste, leeres Set/Frozenset + +.. note:: Leere Strings sind **nicht** erlaubt. + + +integer / int +------------- + +- ``nullable`` + + bool (Default: False): allow also a None/null/nil + +- ``minValue`` +- ``maxValue`` +- ``value`` + + A callable to validate the integer value + +- ``enum`` + + Eine Liste von ganzen Zahlen, von denen genau einer dem vorhandenen + Wert entsprechen muß. + + Achtung: Alle anderen Prüfungen (`minValue`, `maxValue`, `value`) + werden trotzdem auch durchgeführt. + + +real / double / float +--------------------- + +- ``nullable`` + + bool (Default: False): allow also a None/null/nil + +- ``minValue`` +- ``maxValue`` +- ``value`` + + A callable to validate the float value + + +number / num +------------ + +- ``nullable`` + + bool (Default: False): allow also a None/null/nil + +Any numeric value (int or float) + +- ``minValue`` +- ``maxValue`` +- ``value`` + + A callable to validate the number + +- ``enum`` + + Eine Liste von Zahlen, von denen genau einer dem vorhandenen + Wert entsprechen muß. + + Achtung: Alle anderen Prüfungen (`minValue`, `maxValue`, `value`) + werden trotzdem auch durchgeführt. + + +scalar +------ + +Any scalar value: no `None`, no `dict`, no `tuple`, no `list`, no `set`, +no `frozenset`. + +But if + +- ``nullable`` + + bool (Default: False): None/null/nil is allowed also + + +Schema-Kombinatoren +------------------- + +- ``all-of`` + + alle in der gegebenen Liste müssen validieren + +- ``any-of`` + + mindestens einer muß validieren + + Nach den ersten erfolgreichen Test werden alle weiteren Sub-Tests + abgebrochen (aka. short-circuit Verhalten). + +- ``one-of`` + + **genau einer** aus der Liste muß validieren (aka. xor) + +- ``not`` + + das folgende Schema darf nicht successful validieren + + +Bedingungen +=========== + +``cond``-Key im Schema: + + Lisp-like `cond`: + + - eine Liste von Wenn-Dann-Paaren + + Bedingung: ``when``, ``when-ref-true``, ``when-ref-exists`` + + Dann: ``then``, ``then-merge`` + + Für ``when``: + + Logische Operatoren: + + ``not`` + + ``all-of`` (aka `and`) + + ``any-of`` (aka `or`) + + ``one-of`` (aka `xor`) + + Prädikate: + + ``ref-true``, ``ref-exists``, ein Objekt im boolschen Kontext + + Vergleichs-Operator: + + ``equals`` gefolgt von einer Liste der Länge zwei als Gleichheits- + Operator: + + Mögliche Keys: + + ``ref``: eine Referenz + + ``value`` oder ``val`` ein Wert + + z.B. in YAML:: + + equals: + - ref: object:#my.key + - value: "a string value" + + ``when-ref-true`` und ``when-ref-exists`` sind einfache Abkürzungen für:: + + when: + ref-true: ... + + bzw:: + + when: + ref-exists: ... + + - die *erste* zutreffende Bedingung bestimmt via seinem "Dann" ein Schema + + ``then`` + + Keys im Then-Schema *ersetzen* korrespondierende Keys im Parent-Schema + + ``then-merge`` + + Then-Merge-Schema wird in das Parent-Schema *eingemischt* + + - das ganze erfolgt rekursiv + + - falls keine der Bedingungen zutrifft wird nichts ausgeführt/geändert + + - ``when`` -- direkt gefolgt von einer Liste -- ist eine Abkürzung für + ``all-of` und eben dieser Liste:: + + cond: + when: + - test1 + - test2 + - test3 + + ist äquivalent zu:: + + cond: + when: + all-of: + - test1 + - test2 + - test3 + +.. important:: Schema-Referenzen werden **vor** dem Replace/Merge jeweils + aufgelöst! + +``match`` entspricht ``cond`` -- mit dem Unterschied, daß statt der *ersten* +wahren Bedingung **alle** wahren Bedingungen ausgeführt werden; + + erst werden alle Schemata, die aus wahren Bedingungen kommen gesammelt, + danach werden die Schemata ersetzt bzw. gemerged. + +Beispiel:: + + required: + - a + - b + cond: + - when: + all-of: + - not: + ref-true: 'object:#p1.p2.p3' + - ref-exists: '#p4.p5' + then: + required: ["foo", "bar"] # replace existing `required' + - when: + ref-true: 'object:#p6.p7' + then: + new-key: "new-val" # a new key to the containing dict + then-merge: + required: ["c", "d"] # add `c' and `d' to `a' and `b' + - when: true # als letzer Fall: "else" + then-replace: + required: ["something", "else"] # replace existing `required' + + +Referenzen +========== + +URI-Syntax + + Angepaßte und simplifizierte JSON-Pointer-Syntax (:rfc:`6901`) + +Beispiele: + + - ``object:#wsgi.china_detector.enabled`` + + ist (weil `object` das Default-URI-Schema ist) äquivalent zu: + + ``#wsgi.china_detector.enabled`` + + Das ist eine **absolute** Referenz. + + ``.`` ist also -- wie in :py:mod:`configmix` -- der Hierarchie-Separator + für URI-Fragmente in Objekt-Referenzen + + - ``object:#`` ist das Root-Objekt + + - ``object:#.`` ist das current Kontext-Object (aka "Base") + + - ``object:`` ist *ungültig* + + Ein Fragment **muß** also formal vorhanden sein -- auch wenn es leer ist. + + - Relative Referenzen *starten* mit einen Punkt (analog Python-Imports) + + Mehrere führende Punkte sind -- wie bei Python-Imports -- relative + Referenzen zu Parent-Objekten. Der Versuch, den Parent des Root-Objektes + anzusprechen, liefert einen :py:exc:`TypeError`. + +Wo ein Schema erlaubt ist, ist auch ein dict mit dem einzigen Key ``$ref`` +erlaubt. Dies ist eine Referenz auf ein anderes Schema mit dem URI-Schema +``schema:``. Dieses andere Schema kann auch aus einer anderen Datei kommen: + + - ``schema:$root#/`` + + Das Root-Element des Root-Schemas + + - ``schema:$self#/`` + + Das Root-Element des gerade aktiven Schemas. + + - ``schema:data:schemalib:file.schema.yml#/foo`` + + Das ``foo``-Element des via Packagedata von `schemalib` geladenen Schemas + `file.schema.yml`. Das ist dann auch das neue aktive Schema.
