Skip to content

Commit 811ac21

Browse files
authored
Merge pull request #121 from labthings/docs-reorg
Docs reorg
2 parents a3a66ad + d039874 commit 811ac21

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+1875
-952
lines changed

.coveragerc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[run]
22
branch = True
33
source = ./src/labthings
4-
omit = .venv/*, ./src/labthings/wsgi.py, ./src/labthings/monkey.py, ./src/labthings/server/*, ./src/labthings/core/*
4+
omit = .venv/*, ./src/labthings/wsgi.py, ./src/labthings/monkey.py, ./src/labthings/server/*, ./src/labthings/core/*, ./src/labthings/example_components/*
55
concurrency = thread
66

77
[report]

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
* Code formatting ([a86bc21](https://github.com/labthings/python-labthings/commit/a86bc21))
4040
* Code formatting ([9399433](https://github.com/labthings/python-labthings/commit/9399433))
4141
* Code formatting ([cc676b7](https://github.com/labthings/python-labthings/commit/cc676b7))
42-
* Created a current_thing LocalProxy ([2b421b8](https://github.com/labthings/python-labthings/commit/2b421b8))
42+
* Created a current_labthing() LocalProxy ([2b421b8](https://github.com/labthings/python-labthings/commit/2b421b8))
4343
* Deleted test file ([2585abd](https://github.com/labthings/python-labthings/commit/2585abd))
4444
* Fix HTTP method check ([8ae6060](https://github.com/labthings/python-labthings/commit/8ae6060))
4545
* FIx OpenAPI formatting ([c90a646](https://github.com/labthings/python-labthings/commit/c90a646))

docs/api.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
API Reference
2+
=============
3+
4+
.. automodule:: labthings
5+
:members:
File renamed without changes.

docs/basic_usage/app_thing_server.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
App, LabThing, and Server
2+
=========================
3+
4+
Python LabThings works as a Flask extension, and so we introduce two key objects: the :class:`flask.Flask` app, and the :class:`labthings.LabThing` object. The :class:`labthings.LabThing` object is our main entrypoint for the Flask application, and all LabThings functionality is added via this object.
5+
6+
In order to enable threaded actions, and concurrent WebSocket connections, the app should be served using the :class:`labthings.Server` class. Other production servers such as Gevent can be used, however this will require monkey-patching and has not been comprehensively tested.
7+
8+
9+
Create app
10+
----------
11+
12+
The :meth:`labthings.create_app` function automatically creates a Flask app object, enables up cross-origin resource sharing, and initialises a :class:`labthings.LabThing` instance on the app. The function returns both in a tuple.
13+
14+
.. autofunction:: labthings.create_app
15+
:noindex:
16+
17+
ALternatively, the app and :class:`labthings.LabThing` objects can be initialised and attached separately, for example:
18+
19+
.. code-block:: python
20+
21+
from flask import Flask
22+
from labthings import LabThing
23+
24+
app = Flask(__name__)
25+
labthing = LabThing(app)
26+
27+
28+
LabThing
29+
--------
30+
31+
The LabThing object is our main entrypoint, and handles creating API views, managing background actions, tracking logs, and generating API documentation.
32+
33+
.. autoclass:: labthings.LabThing
34+
:noindex:
35+
36+
Two key methods are :meth:`labthings.LabThing.build_property` and :meth:`labthings.LabThing.build_action`. These methods allow the automation creation of Property and Action API views from Python object attributes and methods. By passing schemas to these methods, argument and response marshalling is automatically performed. Offloading actions to background threads is also handled automatically.
37+
38+
.. automethod:: labthings.LabThing.build_property
39+
:noindex:
40+
41+
.. automethod:: labthings.LabThing.build_action
42+
:noindex:
43+
44+
45+
Server
46+
------
47+
48+
The integrated server actually handles 3 distinct server functions: WSGI HTTP requests, routing WebSocket requests to the threaded handler, and registering mDNS records for automatic Thing discovery. It is therefore strongly suggested you use the builtin server.
49+
50+
**Important notes:**
51+
52+
The integrated server will spawn a new native thread *per-connection*. This will only function well in situations where few (<50) simultaneous connections are expected, such as local Web of Things devices. Do not use this server in any public web app where many connections are expected. It is designed exclusively with low-traffic LAN access in mind.
53+
54+
.. autoclass:: labthings.Server
55+
:members:
56+
:noindex:

docs/basic_usage/http_api_structure.rst

Whitespace-only changes.

docs/basic_usage/index.rst

Lines changed: 10 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,13 @@
11
Basic usage
22
===========
33

4-
The easiest way to get started with Python-LabThings is via the :mod:`labthings.quick` module, and the :class:`labthings.LabThing` builder methods.
5-
6-
We will assume that for basic usage you already have some basic instrument control code. In our example, this is in the form of a ``PretendSpectrometer`` class, which will generate some data like your instrument control code might. Our ``PretendSpectrometer`` class has a ``data`` property which quickly returns a spectrum, an ``x_range`` property which determines the range of data we'll return, a ``magic_denoise`` property for cleaning up our signal, and a slow ``average_data(n)`` method to average ``n`` individual data measurements.
7-
8-
Building an API from this class requires a few extra considerations. In order to tell our API what data to expect from users, we need to construct a schema for each of our interactions. This schema simply maps variable names to JSON-compatible types, and is made simple via the :mod:`labthings.fields` module.
9-
10-
For properties, the input and output MUST be formatted the same, and so a single ``schema`` argument handles both. For actions, the input parameters and output response may be different. In this case, we can pass a ``schema`` argument to format the output, and an ``args`` argument to specify the input parameters,
11-
12-
An example Lab Thing built from our ``PretendSpectrometer`` class, complete with schemas, might look like:
13-
14-
15-
.. code-block:: python
16-
17-
from labthings.server.quick import create_app
18-
from labthings.server import fields
19-
20-
from my_components import PretendSpectrometer
21-
22-
23-
# Create LabThings Flask app
24-
app, labthing = create_app(
25-
__name__,
26-
title="My PretendSpectrometer API",
27-
description="LabThing API for PretendSpectrometer",
28-
version="0.1.0"
29-
)
30-
31-
32-
# Make some properties and actions out of our component
33-
my_spectrometer = PretendSpectrometer()
34-
35-
# Single-shot data property
36-
labthing.build_property(
37-
my_spectrometer, # Python object
38-
"data", # Objects attribute name
39-
description="A single-shot measurement",
40-
readonly=True,
41-
schema=fields.List(fields.Number())
42-
)
43-
44-
# Magic denoise property
45-
labthing.build_property(
46-
my_spectrometer, # Python object
47-
"magic_denoise", # Objects attribute name
48-
description="A magic denoise property",
49-
schema=fields.Int(min=100, max=500, example=200)
50-
)
51-
52-
# Averaged measurement action
53-
labthing.build_action(
54-
my_spectrometer.average_data, # Python function
55-
description="Take an averaged measurement",
56-
args={ # How do we convert from the request input to function arguments?
57-
"n": fields.Int(description="Number of averages to take", example=5, default=5)
58-
},
59-
)
60-
61-
62-
# Start the app
63-
if __name__ == "__main__":
64-
from labthings.server.wsgi import Server
65-
Server(app).run()
66-
67-
68-
Once started, the app will build and serve a full web API, and generate the following Thing Description:
69-
70-
.. code-block:: JSON
71-
72-
{
73-
"@context": [
74-
"https://www.w3.org/2019/wot/td/v1",
75-
"https://iot.mozilla.org/schemas/"
76-
],
77-
"id": "http://127.0.0.1:7486/",
78-
"base": "http://127.0.0.1:7486/",
79-
"title": "My PretendSpectrometer API",
80-
"description": "LabThing API for PretendSpectrometer",
81-
"properties": {
82-
"pretendSpectrometerData": {
83-
"title": "PretendSpectrometer_data",
84-
"description": "A single-shot measurement",
85-
"readOnly": true,
86-
"links": [{
87-
"href": "/properties/PretendSpectrometer/data"
88-
}],
89-
"forms": [{
90-
"op": "readproperty",
91-
"htv:methodName": "GET",
92-
"href": "/properties/PretendSpectrometer/data",
93-
"contentType": "application/json"
94-
}],
95-
"type": "array",
96-
"items": {
97-
"type": "number",
98-
"format": "decimal"
99-
}
100-
},
101-
"pretendSpectrometerMagicDenoise": {
102-
"title": "PretendSpectrometer_magic_denoise",
103-
"description": "A magic denoise property",
104-
"links": [{
105-
"href": "/properties/PretendSpectrometer/magic_denoise"
106-
}],
107-
"forms": [{
108-
"op": "readproperty",
109-
"htv:methodName": "GET",
110-
"href": "/properties/PretendSpectrometer/magic_denoise",
111-
"contentType": "application/json"
112-
},
113-
{
114-
"op": "writeproperty",
115-
"htv:methodName": "PUT",
116-
"href": "/properties/PretendSpectrometer/magic_denoise",
117-
"contentType": "application/json"
118-
}
119-
],
120-
"type": "number",
121-
"format": "integer",
122-
"min": 100,
123-
"max": 500,
124-
"example": 200
125-
}
126-
},
127-
"actions": {
128-
"averageDataAction": {
129-
"title": "average_data_action",
130-
"description": "Take an averaged measurement",
131-
"links": [{
132-
"href": "/actions/PretendSpectrometer/average_data"
133-
}],
134-
"forms": [{
135-
"op": "invokeaction",
136-
"htv:methodName": "POST",
137-
"href": "/actions/PretendSpectrometer/average_data",
138-
"contentType": "application/json"
139-
}],
140-
"input": {
141-
"type": "object",
142-
"properties": {
143-
"n": {
144-
"type": "number",
145-
"format": "integer",
146-
"default": 5,
147-
"description": "Number of averages to take",
148-
"example": 5
149-
}
150-
}
151-
}
152-
}
153-
},
154-
"links": [...],
155-
"securityDefinitions": {...},
156-
"security": "nosec_sc"
157-
}
158-
159-
160-
For completeness of the examples, our ``PretendSpectrometer`` class code is:
161-
162-
.. code-block:: python
163-
164-
import random
165-
import math
166-
import time
167-
168-
class PretendSpectrometer:
169-
def __init__(self):
170-
self.x_range = range(-100, 100)
171-
self.magic_denoise = 200
172-
173-
def make_spectrum(self, x, mu=0.0, sigma=25.0):
174-
"""
175-
Generate a noisy gaussian function (to act as some pretend data)
176-
177-
Our noise is inversely proportional to self.magic_denoise
178-
"""
179-
x = float(x - mu) / sigma
180-
return (
181-
math.exp(-x * x / 2.0) / math.sqrt(2.0 * math.pi) / sigma
182-
+ (1 / self.magic_denoise) * random.random()
183-
)
184-
185-
@property
186-
def data(self):
187-
"""Return a 1D data trace."""
188-
return [self.make_spectrum(x) for x in self.x_range]
189-
190-
def average_data(self, n: int):
191-
"""Average n-sets of data. Emulates a measurement that may take a while."""
192-
summed_data = self.data
193-
194-
for _ in range(n):
195-
summed_data = [summed_data[i] + el for i, el in enumerate(self.data)]
196-
time.sleep(0.25)
197-
198-
summed_data = [i / n for i in summed_data]
199-
200-
return summed_data
4+
.. toctree::
5+
:maxdepth: 2
6+
:caption: Contents:
7+
8+
app_thing_server.rst
9+
http_api_structure.rst
10+
websocket_api_structure.rst
11+
serialising.rst
12+
action_tasks.rst
13+
synchronisation.rst

docs/basic_usage/serialising.rst

Whitespace-only changes.

docs/basic_usage/synchronisation.rst

Whitespace-only changes.

docs/basic_usage/ws_api_structure.rst

Whitespace-only changes.

0 commit comments

Comments
 (0)