Skip to content

Commit

Permalink
Expanded documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Joel Collins committed Jul 20, 2020
1 parent 1af31b4 commit 17d4f23
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ labthing.build_action(
my_spectrometer, # Python object
"average_data", # Objects method name
description="Take an averaged measurement",
schema=fields.List(fields.Number()),
args={ # How do we convert from the request input to function arguments?
"n": fields.Int(description="Number of averages to take", example=5, default=5)
},
Expand Down
4 changes: 4 additions & 0 deletions docs/basic_usage/http_api_structure.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HTTP API Structure
==================

*Documentation to be written*
83 changes: 83 additions & 0 deletions docs/basic_usage/serialising.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
Marshalling and Serialising views
=================================

Introduction
------------

LabThings makes use of the `Marshmallow library <https://github.com/marshmallow-code/marshmallow/>`_ for both response and argument marshaling. From the Marshmallow documentation:

**marshmallow** is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.

In short, marshmallow schemas can be used to:

- **Validate** input data.
- **Deserialize** input data to app-level objects.
- **Serialize** app-level objects to primitive Python types. The serialized objects can then be rendered to standard formats such as JSON for use in an HTTP API.

Marshalling schemas are used by LabThings to document the data types of properties, as well as the structure and types of Action arguments and return values. They allow arbitrary Python objects to be returned as serialized JSON, and ensure that input arguments are properly formated before being passed to your Python functions.

From our quickstart example, we use schemas for our `integration_time` property to inform LabThings that both responses *and* requests to the API should be integer formatted. Additional information about range, example values, and units can be added to the schema field.

.. code-block:: python
labthing.build_property(
my_spectrometer, # Python object
"integration_time", # Objects attribute name
description="A magic denoise property",
schema=fields.Int(min=100, max=500, example=200, unit="microsecond")
)
Actions require separate schemas for input and output, since the action return data is likely in a different format to the input arguments. In our quickstart example, our `schema` argument informs LabThings that the action return value should be a list of numbers. Meanwhile, our `args` argument informs LabThings that requests to start the action should include an attribute called `n`, which should be an integer. Human-readable descriptions, examples, and default values can be added to the args field.

.. code-block:: python
labthing.build_action(
my_spectrometer, # Python object
"average_data", # Objects method name
description="Take an averaged measurement",
schema=fields.List(fields.Number()),
args={ # How do we convert from the request input to function arguments?
"n": fields.Int(description="Number of averages to take", example=5, default=5)
},
)
Schemas
-------

A schema is a collection of keys and fields describing how an object should be serialized/deserialized. Schemas can be created in several ways, either by creating a :class:`labthings.Schema` class, or by passing a dictionary of key-field pairs.

Note that the :class:`labthings.Schema` class is an alias of :class:`marshmallow.Schema`, and the two can be used interchangeably.

Schemas are required for argument parsing. While Property views can be marshalled with a single field, arguments must be passed to the server as a JSON object, which gets mapped to a Python dictionary and passed to the Action method.

For example, a Python function of the form:

.. code-block:: python
from typing import List
def my_function(quantity: int, name: str, organizations: List(str)):
return quantity * len(organizations)
would require `args` of the form:

.. code-block:: python
args = {
"quantity": fields.Int()
"name": fields.String()
"organisation": fields.List(fields.String())
}
and a `schema` of :class:`labthings.fields.Int`.


Fields
------

Most data types are represented by fields in the Marshmallow library. All Marshmallow fields are imported and available from the :mod:`labthings.fields` submodule, however any field can be imported from Marshmallow and used in LabThings schemas.

.. automodule:: labthings.fields
:members:
:undoc-members:
4 changes: 4 additions & 0 deletions docs/basic_usage/ws_api_structure.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
WebSocket API Structure
=======================

*Documentation to be written*
1 change: 1 addition & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ An example Lab Thing built from our ``PretendSpectrometer`` class, complete with
my_spectrometer, # Python object
"average_data", # Objects method name
description="Take an averaged measurement",
schema=fields.List(fields.Number()),
args={ # How do we convert from the request input to function arguments?
"n": fields.Int(description="Number of averages to take", example=5, default=5)
},
Expand Down
1 change: 1 addition & 0 deletions examples/docs_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
my_spectrometer, # Python object
"average_data", # Objects method name
description="Take an averaged measurement",
schema=fields.List(fields.Number()),
args={ # How do we convert from the request input to function arguments?
"n": fields.Int(description="Number of averages to take", example=5, default=5)
},
Expand Down
8 changes: 5 additions & 3 deletions src/labthings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
from .tasks import update_task_data
from .tasks import TaskKillException

# Schema and field
from .schema import Schema
from . import fields

# Submodules
from . import extensions
from . import views
from . import fields
from . import schema
from . import semantics
from . import json

Expand All @@ -53,7 +55,7 @@
"extensions",
"views",
"fields",
"schema",
"Schema",
"semantics",
"json",
]
Expand Down
5 changes: 4 additions & 1 deletion src/labthings/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@


class Bytes(Field):
""" """
"""
Marshmallow field for `bytes` objects
"""

def _jsonschema_type_mapping(self):
""" """
return {"type": "string", "contentEncoding": "base64"}
Expand Down

0 comments on commit 17d4f23

Please sign in to comment.