Skip to content

Commit 17d4f23

Browse files
author
Joel Collins
committed
Expanded documentation
1 parent 1af31b4 commit 17d4f23

File tree

8 files changed

+103
-4
lines changed

8 files changed

+103
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ labthing.build_action(
5656
my_spectrometer, # Python object
5757
"average_data", # Objects method name
5858
description="Take an averaged measurement",
59+
schema=fields.List(fields.Number()),
5960
args={ # How do we convert from the request input to function arguments?
6061
"n": fields.Int(description="Number of averages to take", example=5, default=5)
6162
},
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
HTTP API Structure
2+
==================
3+
4+
*Documentation to be written*

docs/basic_usage/serialising.rst

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
Marshalling and Serialising views
2+
=================================
3+
4+
Introduction
5+
------------
6+
7+
LabThings makes use of the `Marshmallow library <https://github.com/marshmallow-code/marshmallow/>`_ for both response and argument marshaling. From the Marshmallow documentation:
8+
9+
**marshmallow** is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.
10+
11+
In short, marshmallow schemas can be used to:
12+
13+
- **Validate** input data.
14+
- **Deserialize** input data to app-level objects.
15+
- **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.
16+
17+
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.
18+
19+
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.
20+
21+
.. code-block:: python
22+
23+
labthing.build_property(
24+
my_spectrometer, # Python object
25+
"integration_time", # Objects attribute name
26+
description="A magic denoise property",
27+
schema=fields.Int(min=100, max=500, example=200, unit="microsecond")
28+
)
29+
30+
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.
31+
32+
.. code-block:: python
33+
34+
labthing.build_action(
35+
my_spectrometer, # Python object
36+
"average_data", # Objects method name
37+
description="Take an averaged measurement",
38+
schema=fields.List(fields.Number()),
39+
args={ # How do we convert from the request input to function arguments?
40+
"n": fields.Int(description="Number of averages to take", example=5, default=5)
41+
},
42+
)
43+
44+
45+
Schemas
46+
-------
47+
48+
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.
49+
50+
Note that the :class:`labthings.Schema` class is an alias of :class:`marshmallow.Schema`, and the two can be used interchangeably.
51+
52+
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.
53+
54+
For example, a Python function of the form:
55+
56+
.. code-block:: python
57+
58+
from typing import List
59+
60+
def my_function(quantity: int, name: str, organizations: List(str)):
61+
return quantity * len(organizations)
62+
63+
would require `args` of the form:
64+
65+
.. code-block:: python
66+
67+
args = {
68+
"quantity": fields.Int()
69+
"name": fields.String()
70+
"organisation": fields.List(fields.String())
71+
}
72+
73+
and a `schema` of :class:`labthings.fields.Int`.
74+
75+
76+
Fields
77+
------
78+
79+
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.
80+
81+
.. automodule:: labthings.fields
82+
:members:
83+
:undoc-members:

docs/basic_usage/ws_api_structure.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
WebSocket API Structure
2+
=======================
3+
4+
*Documentation to be written*

docs/quickstart.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ An example Lab Thing built from our ``PretendSpectrometer`` class, complete with
5252
my_spectrometer, # Python object
5353
"average_data", # Objects method name
5454
description="Take an averaged measurement",
55+
schema=fields.List(fields.Number()),
5556
args={ # How do we convert from the request input to function arguments?
5657
"n": fields.Int(description="Number of averages to take", example=5, default=5)
5758
},

examples/docs_example.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
my_spectrometer, # Python object
3737
"average_data", # Objects method name
3838
description="Take an averaged measurement",
39+
schema=fields.List(fields.Number()),
3940
args={ # How do we convert from the request input to function arguments?
4041
"n": fields.Int(description="Number of averages to take", example=5, default=5)
4142
},

src/labthings/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
from .tasks import update_task_data
2626
from .tasks import TaskKillException
2727

28+
# Schema and field
29+
from .schema import Schema
30+
from . import fields
31+
2832
# Submodules
2933
from . import extensions
3034
from . import views
31-
from . import fields
32-
from . import schema
3335
from . import semantics
3436
from . import json
3537

@@ -53,7 +55,7 @@
5355
"extensions",
5456
"views",
5557
"fields",
56-
"schema",
58+
"Schema",
5759
"semantics",
5860
"json",
5961
]

src/labthings/fields.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@
7272

7373

7474
class Bytes(Field):
75-
""" """
75+
"""
76+
Marshmallow field for `bytes` objects
77+
"""
78+
7679
def _jsonschema_type_mapping(self):
7780
""" """
7881
return {"type": "string", "contentEncoding": "base64"}

0 commit comments

Comments
 (0)