Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e282676
Initialise all Things during ThingServer.__init__
rwb27 Oct 27, 2025
15dce7b
Tidy up config models and make them available at module level.
rwb27 Oct 27, 2025
5399569
Tidy up Thing lifecycle and leave validation to the Pydantic models
rwb27 Oct 27, 2025
d9689a0
Don't print stack traces for validation errors.
rwb27 Oct 27, 2025
f3aedd8
Update test code to pass Thing classes to ThingServer.__init__
rwb27 Oct 27, 2025
869d7ac
Update example/documentation code examples.
rwb27 Oct 27, 2025
3a5aef1
Fix up some docstrings.
rwb27 Oct 27, 2025
c7786df
Rename thing_connection to thing_slot
rwb27 Oct 27, 2025
728baa5
Remove the defunct object_reference_to_object
rwb27 Oct 27, 2025
655bf9d
Remove an unused import
rwb27 Oct 28, 2025
1c23151
Typing fixes for the fallback server
rwb27 Oct 28, 2025
b1b98e2
Update index and core concepts
rwb27 Oct 28, 2025
c496f96
Allow specifying folder in create_thing_without_server
rwb27 Oct 28, 2025
aa04aff
Spelling fixes in docs.
rwb27 Oct 28, 2025
6aaf7b2
silence a spurious type warning
rwb27 Oct 28, 2025
71e2a38
Update src/labthings_fastapi/server/__init__.py
rwb27 Oct 29, 2025
28e905a
Reformat example to give `things` a name.
rwb27 Oct 29, 2025
21a4529
Add details of what's mocked in MockThingServerInterface.
rwb27 Nov 12, 2025
b88dd81
Delete an unused regex and import
rwb27 Nov 12, 2025
74512ac
Add a comment about the error raised.
rwb27 Nov 12, 2025
1ef4f78
Clearer imports in test code
rwb27 Nov 12, 2025
a4d378d
Delete a commented-out block
rwb27 Nov 12, 2025
a105c2e
Fix a typo in a docstring
rwb27 Nov 12, 2025
a97aeaf
Fix typo in a docstring
rwb27 Nov 12, 2025
53ca584
Check that non-dict objects fail for actions accepting kwargs
rwb27 Nov 13, 2025
de595e1
Add a test for property updates failing without an event loop.
rwb27 Nov 13, 2025
47eac1c
Check slashes are not valid in Thing names
rwb27 Nov 13, 2025
9179a7e
Eliminate `names_set`
rwb27 Nov 13, 2025
48ec1e6
Rename test_thing_connection
rwb27 Nov 13, 2025
bab3dcc
Fix docstring for `_create_things`.
rwb27 Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/source/actions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Actions

Actions are the way `.Thing` objects are instructed to do things. In Python
terms, any method of a `.Thing` that we want to be able to call over HTTP
should be decorated as an Action, using :deco:`.thing_action`.
should be decorated as an Action, using `.thing_action`.

This page gives an overview of how actions are implemented in LabThings-FastAPI.
:ref:`wot_cc` includes a section on :ref:`wot_actions` that introduces the general concept.
Expand Down Expand Up @@ -91,15 +91,17 @@ such that the action code can use module-level symbols rather than needing
to explicitly pass the logger and cancel hook as arguments to the action
method.

Usually, you don't need to consider this mechanism: simply use the invocation
logger or cancel hook as explained above. However, if you want to run actions
Usually, you don't need to consider this mechanism: simply use `.Thing.logger`
or `.cancellable_sleep` as explained above. However, if you want to run actions
outside of the server (for example, for testing purposes) or if you want to
call one action from another action, but not share the cancellation signal
or log, functions are provided in `.invocation_contexts` to manage this.

If you start a new thread from an action, code running in that thread will
not have the invocation ID set in a context variable. A subclass of
not have an invocation ID set in a context variable. A subclass of
`threading.Thread` is provided to do this, `.ThreadWithInvocationID`\ .
This may be useful for test code, or if you wish to run actions in the
background, with the option of cancelling them.

Raising exceptions
------------------
Expand Down
4 changes: 2 additions & 2 deletions docs/source/dependencies/dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Dependencies

.. warning::

The use of dependencies is now deprecated. See :ref:`thing_connections` and `.ThingServerInterface` for a more intuitive way to access that functionality.
The use of dependencies is now deprecated. See :ref:`thing_slots` and `.ThingServerInterface` for a more intuitive way to access that functionality.

LabThings makes use of the powerful "dependency injection" mechanism in FastAPI. You can see the `FastAPI documentation`_ for more information. In brief, FastAPI dependencies are annotated types that instruct FastAPI to supply certain function arguments automatically. This removes the need to set up resources at the start of a function, and ensures everything the function needs is declared and typed clearly. The most common use for dependencies in LabThings is where an action needs to make use of another `.Thing` on the same `.ThingServer`.

Expand All @@ -14,7 +14,7 @@ Inter-Thing dependencies

.. warning::

These dependencies are deprecated - see :ref:`thing_connections` instead.
These dependencies are deprecated - see :ref:`thing_slots` instead.

Simple actions depend only on their input parameters and the `.Thing` on which they are defined. However, it's quite common to need something else, for example accessing another `.Thing` instance on the same LabThings server. There are two important principles to bear in mind here:

Expand Down
9 changes: 6 additions & 3 deletions docs/source/dependencies/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ def increment_counter(self, my_thing: MyThingDep) -> None:
my_thing.increment_counter()


server = lt.ThingServer()
server.add_thing("mything", MyThing)
server.add_thing("testthing", TestThing)
server = lt.ThingServer(
{
"mything": MyThing,
"testthing": TestThing,
}
)

if __name__ == "__main__":
import uvicorn
Expand Down
35 changes: 15 additions & 20 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ Documentation for LabThings-FastAPI

quickstart/quickstart.rst
wot_core_concepts.rst
lt_core_concepts.rst
structure.rst
tutorial/index.rst
examples.rst
actions.rst
thing_connections.rst
thing_slots.rst
dependencies/dependencies.rst
blobs.rst
concurrency.rst
Expand All @@ -20,26 +20,21 @@ Documentation for LabThings-FastAPI

autoapi/index

`labthings-fastapi` implements a Web of Things interface for laboratory hardware using Python. This is a ground-up rewrite of python-labthings_, replacing Flask 1 and Marshmallow with FastAPI and Pydantic. It is the underlying framework for v3 of the `OpenFlexure Microscope software <https://gitlab.com/openflexure/openflexure-microscope-server/>`_.
`labthings-fastapi` is a Python library to simplify the process of making laboratory instruments available via a HTTP. It aims to create an API that is usable from any modern programming language, with API documentation in both :ref:`openapi` and :ref:`gen_td` formats. It is the underlying framework for v3 of the `OpenFlexure Microscope software <https://gitlab.com/openflexure/openflexure-microscope-server/>`_. Key features and design aims are:

`labthings-fastapi` aims to simplify the process of making laboratory instruments available via an HTTP API. Key features and design aims are below:

* Functionality together in `Thing` subclasses, which represent units of hardware or software (see :doc:`wot_core_concepts`)
* Methods and properties of `Thing` subclasses may be added to the HTTP API and Thing Description using decorators
* The functionality of a unit of hardware or software is described using `.Thing` subclasses.
* Methods and properties of `.Thing` subclasses may be added to the HTTP API and associated documentation using decorators.
* Datatypes of action input/outputs and properties are defined with Python type hints.
* Actions are decorated methods of a `.Thing` class. There is no need for separate schemas or endpoint definitions.
* Properties are defined either as typed attributes (similar to `pydantic` or `dataclasses`) or with a `property`\ -like decorator.
* Lifecycle and concurrency are appropriate for hardware: `Thing` code is always run in a thread, and each `Thing` is instantiated, started up, and shut down only once.
* Vocabulary and concepts are aligned with the `W3C Web of Things <https://www.w3.org/WoT/>`_ standard (see :doc:`wot_core_concepts`)
- Things are classes, with properties and actions defined exactly once
- Thing Descriptions are automatically generated, and validated with `pydantic`
- OpenAPI documentation is automatically generated by FastAPI
* We follow FastAPI_'s lead and try to use standard Python features to minimise unnecessary code
- Datatypes of action input/outputs and properties are defined with Python type hints
- Actions are defined exactly once, as a method of a `Thing` class
- Properties and actions are declared using decorators (or descriptors if that's preferred)
- FastAPI_ "Dependency injection" is used to manage relationships between Things and dependency on the server
* Lifecycle and concurrency are appropriate for hardware: `Thing` code is always run in a thread, and each `Thing` is instantiated and shut down only once.
- Starlette (used by FastAPI) can handle requests asynchronously - this improves performance and enables websockets and other long-lived connections.
- `Thing` code is still, for now, threaded. In the future it may become possible to us other concurrency models in `Thing` code.

Compared to `python-labthings`_, this framework updates dependencies, shrinks the codebase, and simplifies the API (see :doc:`lt_core_concepts`).

Previous version
----------------

This is a ground-up rewrite of python-labthings_, replacing Flask 1 and Marshmallow with FastAPI and Pydantic.
Compared to `python-labthings`_, this framework updates dependencies, shrinks the codebase, and simplifies the API (see :doc:`lt_structure`).
* FastAPI more or less completely eliminates OpenAPI generation code from our codebase
* Marshmallow schemas and endpoint classes are replaced with Python type hints, eliminating double- or triple-definition of actions and their inputs/outputs.
* Thing Description generation is very much simplified by the new structure (multiple Things instead of one massive Thing with many extensions)
Expand Down
61 changes: 0 additions & 61 deletions docs/source/lt_core_concepts.rst

This file was deleted.

5 changes: 1 addition & 4 deletions docs/source/quickstart/counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ def slowly_increase_counter(self) -> None:
if __name__ == "__main__":
import uvicorn

server = lt.ThingServer()

# The line below creates a TestThing instance and adds it to the server
server.add_thing("counter", TestThing)
server = lt.ThingServer({"counter": TestThing})

# We run the server using `uvicorn`:
uvicorn.run(server.app, port=5000)
42 changes: 42 additions & 0 deletions docs/source/structure.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.. _labthings_cc:
.. _labthings_structure:

LabThings structure
===================

LabThings is intended to simplify the process of making a piece of hardware available through an HTTP API and documenting that API with :ref:`gen_docs`\ .

Server
------

LabThings is a server-based framework.
The `.ThingServer` creates and manages the `.Thing` instances that represent individual hardware or software units. The functionality of those `.Thing`\ s is accessed via HTTP requests, which can be made from a web browser, the command line, or any programming language with an HTTP library.

LabThings-FastAPI is built on top of `fastapi`\ , which is a fast, modern HTTP framework. LabThings provides functionality to manage `.Thing`\ s and their actions, including:

* Initialising, starting up, and shutting down the `.Thing` instances, so that hardware is correctly started up and shut down.
* Managing actions, including making logs and output values available over HTTP.
* Managing `.Blob` input and output (i.e. binary objects that are best not serialised to JSON).
* Generating a :ref:`gen_td` in addition to the :ref:`openapi` documentation produced by `fastapi`\ .
* Making connections between `.Thing` instances as required.

`.Thing`\ s
-----------

Each unit of hardware (or software) that should be exposed by the server is implemented as a subclass of `.Thing`\ . A `.Thing` subclass represents a particular type of instrument (whether hardware or software), and its functionality is described using actions and properties, described below. `.Thing`\ s don't have to correspond to separate pieces of hardware: it's possible (and indeed recommended) to use `.Thing` subclasses for software components, plug-ins, swappable modules, or anything else that needs to add functionality to the server. `.Thing`\ s may access each other's attributes, so you can write a `.Thing` that implements a particular measurement protocol or task, using hardware that's accessed through other `.Thing` instances on the server. Each `.Thing` is documented by a :ref:`gen_td` which outlines its features in a higher-level way than :ref:`openapi`\ .

The attributes of a `.Thing` are made available over HTTP by decorating or marking them with the following functions:

* `.property` may be used as a decorator analogous to Python's built-in ``@property``\ . It can also be used to mark class attributes as variables that should be available over HTTP.
* `.setting` works similarly to `.property` but it is persisted to disk when the server stops, so the value is remembered.
* `.thing_action` is a decorator that makes methods available over HTTP.
* `.thing_slot` tells LabThings to supply an instance of another `.Thing` at runtime, so your `.Thing` can make use of it.

Client Code
-----------

Client code can be written in any language that supports an HTTP request. However, LabThings FastAPI provides additional functionality that makes writing client code in Python easier.

`.ThingClient` is a class that wraps up the required HTTP requests into a simpler interface. It can retrieve the :ref:`gen_td` over HTTP and use it to generate a new object with methods matching each `.thing_action` and properties matching each `.property`.

While the current dynamic implementation of `.ThingClient` can be inspected with functions like `help` at runtime, it does not work well with static tools like `mypy` or `pyright`\ . In the future, LabThings should be able to generate static client code that works better with autocompletion and type checking.
Loading
Loading