labthings_fastapi.thing_description =================================== .. py:module:: labthings_fastapi.thing_description .. autoapi-nested-parse:: Thing Description module. This module supports the generation of Thing Descriptions. Currently, the top level function lives in `.Thing.thing_description`, but most of the supporting code is in this submodule. A Pydantic model implementing the Thing Description is in `.thing_description._model`, and this is used to generate our TDs - using a `pydantic.BaseModel` helps make sure any TD errors get caught when they are generated in Python, which makes them much easier to debug. We also use the JSONSchema provided by W3C to validate the TDs we generate, in `.thing_description.validation`, as a double-check that we are standards-compliant. Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/labthings_fastapi/thing_description/_model/index /autoapi/labthings_fastapi/thing_description/validation/index Attributes ---------- .. autoapisummary:: labthings_fastapi.thing_description.JSONSchema Classes ------- .. autoapisummary:: labthings_fastapi.thing_description.DataSchema Functions --------- .. autoapisummary:: labthings_fastapi.thing_description.is_a_reference labthings_fastapi.thing_description.look_up_reference labthings_fastapi.thing_description.is_an_object labthings_fastapi.thing_description.convert_object labthings_fastapi.thing_description.convert_anyof labthings_fastapi.thing_description.convert_prefixitems labthings_fastapi.thing_description.convert_additionalproperties labthings_fastapi.thing_description.check_recursion labthings_fastapi.thing_description.jsonschema_to_dataschema labthings_fastapi.thing_description.type_to_dataschema Package Contents ---------------- .. py:class:: DataSchema(/, **data: Any) Bases: :py:obj:`pydantic.BaseModel` Base for several classes describing datatypes. See https://www.w3.org/TR/wot-thing-description11/#dataschema. Create a new model by parsing and validating input data from keyword arguments. Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model. `self` is explicitly positional-only to allow `self` as a field name. .. py:attribute:: field_type :type: Optional[TypeDeclaration] :value: None .. py:attribute:: description :type: Optional[Description] :value: None .. py:attribute:: title :type: Optional[Title] :value: None .. py:attribute:: descriptions :type: Optional[Descriptions] :value: None .. py:attribute:: titles :type: Optional[Titles] :value: None .. py:attribute:: writeOnly :type: Optional[bool] :value: None .. py:attribute:: readOnly :type: Optional[bool] :value: None .. py:attribute:: oneOf :type: Optional[list[DataSchema]] :value: None .. py:attribute:: unit :type: Optional[str] :value: None .. py:attribute:: enum :type: Optional[list] :value: None .. py:attribute:: format :type: Optional[str] :value: None .. py:attribute:: const :type: Optional[Any] :value: None .. py:attribute:: default :type: Optional[Any] :value: None .. py:attribute:: type :type: Optional[Type] :value: None .. py:attribute:: items :type: Optional[Union[DataSchema, List[DataSchema]]] :value: None .. py:attribute:: maxItems :type: Optional[int] :value: None .. py:attribute:: minItems :type: Optional[int] :value: None .. py:attribute:: minimum :type: Optional[Union[int, float]] :value: None .. py:attribute:: maximum :type: Optional[Union[int, float]] :value: None .. py:attribute:: exclusiveMinimum :type: Optional[Union[int, float]] :value: None .. py:attribute:: exclusiveMaximum :type: Optional[Union[int, float]] :value: None .. py:attribute:: multipleOf :type: Optional[Union[int, float]] :value: None .. py:attribute:: properties :type: Optional[Mapping[str, DataSchema]] :value: None .. py:attribute:: required :type: Optional[list[str]] :value: None .. py:attribute:: minLength :type: Optional[int] :value: None .. py:attribute:: maxLength :type: Optional[int] :value: None .. py:attribute:: pattern :type: Optional[str] :value: None .. py:attribute:: contentEncoding :type: Optional[str] :value: None .. py:attribute:: contentMediaType :type: Optional[str] :value: None .. py:attribute:: model_config Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict]. .. py:data:: JSONSchema .. py:function:: is_a_reference(d: JSONSchema) -> bool Return True if a JSONSchema dict is a reference. JSON Schema references are one-element dictionaries with a single key, `$ref`. `pydantic` sometimes breaks this rule and so we don't check that it's a single key. :param d: A JSONSchema dictionary. :return: ``True`` if the dictionary contains ``$ref``. .. py:function:: look_up_reference(reference: str, d: JSONSchema) -> JSONSchema Look up a reference in a JSONSchema. JSONSchema allows references, where chunks of JSON may be reused. Thing Description does not allow references, so we need to resolve them and paste them in-line. This function can only deal with local references, i.e. they must start with ``#`` indicating they belong to the current file. This function first asserts the reference is local (i.e. starts with # so it's relative to the current file), then looks up each path component in turn and returns the resolved chunk of JSON. :param reference: the local reference (should start with ``#``). :param d: the JSONSchema document. :return: the chunk of JSONSchema referenced by ``reference`` in ``d``. :raise KeyError: if the reference is not found in the supplied JSONSchema. :raise NotImplementedError: if the reference does not start with ``"#/`` and thus is not a local reference. .. py:function:: is_an_object(d: JSONSchema) -> bool Determine whether a JSON schema dict is an object. :param d: a chunk of JSONSchema describing a datatype. :return: ``True`` if the ``type`` is ``object``. .. py:function:: convert_object(d: JSONSchema) -> JSONSchema Convert an object from JSONSchema to Thing Description. Convert JSONSchema objects to Thing Description datatypes. Currently, this deletes the ``additionalProperties`` keyword, which is not supported by Thing Description. :param d: the JSONSchema object. :return: a copy of ``d``, with ``additionalProperties`` deleted. .. py:function:: convert_anyof(d: JSONSchema) -> JSONSchema Convert the anyof key to oneof. JSONSchema makes a distinction between "anyof" and "oneof", where the former means "any of these fields can be present" and the latter means "exactly one of these fields must be present". Thing Description does not have this distinction, so we convert ``anyof`` to ``oneof``. :param d: the JSONSchema object. :return: a copy of ``d``, with ``anyOf`` replaced with ``oneOf``. .. py:function:: convert_prefixitems(d: JSONSchema) -> JSONSchema Convert the prefixitems key to items. JSONSchema 2019 (as used by thing description) used `items` with a list of values in the same way that JSONSchema now uses `prefixitems`. JSONSchema 2020 uses `items` to mean the same as `additionalItems` in JSONSchema 2019 - but Thing Description doesn't support the `additionalItems` keyword. This will result in us overwriting additional items, and we raise a ValueError if that happens. This behaviour may be relaxed in the future. :param d: the JSONSchema object. :return: a copy of ``d``, converted to 2019 format as above. :raise KeyError: if we would overwrite an existing ``items`` key. .. py:function:: convert_additionalproperties(d: JSONSchema) -> JSONSchema Move additionalProperties into properties, or remove it. JSONSchema uses ``additionalProperties`` to define optional properties of ``object``\ s. For Thing Descriptions, this should be moved inside the ``properties`` object. :param d: the JSONSchema object. :return: a copy of ``d``, with ``additionalProperties`` moved into ``properties`` or deleted if ``properties`` is not present. .. py:function:: check_recursion(depth: int, limit: int) -> None Check the recursion count is less than the limit. :param depth: the current recursion depth. :param limit: the maximum recursion depth. :raise ValueError: if we exceed the recursion depth. .. py:function:: jsonschema_to_dataschema(d: JSONSchema, root_schema: Optional[JSONSchema] = None, recursion_depth: int = 0, recursion_limit: int = 99) -> JSONSchema Convert a data type description from JSONSchema to Thing Description. :ref:`wot_td` represents datatypes with DataSchemas, which are almost but not quite JSONSchema format. There are two main tasks to convert them: Resolving references -------------------- JSONSchema allows schemas to be replaced with `{"$ref": "#/path/to/schema"}`. Thing Description does not allow this. `dereference_jsonschema_dict` takes a `dict` representation of a JSON Schema document, and replaces all the references with the appropriate chunk of the file. Converting union types ---------------------- JSONSchema can represent `Union` types using the `anyOf` keyword, which is called `oneOf` by Thing Description. It's possible to achieve the same thing in the specific case of array elements, by setting `items` to a list of `DataSchema` objects. This function does not yet do that conversion. This generates a copy of the document, to avoid messing up `pydantic`'s cache. This function runs recursively: to start with, only ``d`` should be provided (the input JSONSchema). We will use the other arguments to keep track of recursion as we convert the schema. :param d: a JSONSchema representation of a datatype. :param root_schema: the whole JSONSchema document, for resolving references. This will be set to ``d`` when the function is called initially. :param recursion_depth: how deeply this function has recursed (starts at zero). :param recursion_limit: how deeply this function is allowed to recurse. :return: the datatype in Thing Description format. This is not yet a `.DataSchema` instance, but may be trivially converted to one with ``DataSchema(**schema)``. .. py:function:: type_to_dataschema(t: type, **kwargs: Any) -> _model.DataSchema Convert a Python type to a Thing Description DataSchema. This makes use of pydantic's `schema_of` function to create a json schema, then applies some fixes to make a DataSchema as per the Thing Description (because Thing Description is almost but not quite compatible with JSONSchema). Additional keyword arguments are added to the DataSchema, and will override the fields generated from the type that is passed in. Typically you'll want to use this for the `title` field. :param t: the Python datatype or `pydantic.BaseModel` subclass. :param \**kwargs: Additional keyword arguments passed to the `.DataSchema` constructor, often including ``title``. :return: a `.DataSchema` representing the type. :raise ValidationError: if the datatype cannot be represented by a `.DataSchema`.