Skip to content

Commit 3e69204

Browse files
committed
Fix links and link to anyio
1 parent dd6095c commit 3e69204

File tree

4 files changed

+21
-14
lines changed

4 files changed

+21
-14
lines changed

docs/source/blobs.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Because :class:`.Blob` outputs are represented in JSON as links, they are downlo
126126
HTTP interface and serialization
127127
--------------------------------
128128

129-
:class:`.Blob` objects are subclasses of `pydantic.BaseModel`, which means they can be serialized to JSON and deserialized from JSON. When this happens, the :class:`.Blob` is represented as a JSON object with :prop:`.Blob.url` and :prop:`.Blob.content_type` fields. The :prop:`.Blob.url` field is a link to the data. The :prop:`.Blob.content_type` field is a string representing the MIME type of the data. It is worth noting that models may be nested: this means an action may return many :class:`.Blob` objects in its output, either as a list or as fields in a :class:`pydantic.BaseModel` subclass. Each :class:`.Blob` in the output will be serialized to JSON with its URL and content type, and the client can then download the data from the URL, one download per :class:`.Blob` object.
129+
:class:`.Blob` objects are subclasses of `pydantic.BaseModel`, which means they can be serialized to JSON and deserialized from JSON. When this happens, the :class:`.Blob` is represented as a JSON object with `.Blob.url` and `.Blob.content_type` fields. The `.Blob.url` field is a link to the data. The `.Blob.content_type` field is a string representing the MIME type of the data. It is worth noting that models may be nested: this means an action may return many :class:`.Blob` objects in its output, either as a list or as fields in a :class:`pydantic.BaseModel` subclass. Each :class:`.Blob` in the output will be serialized to JSON with its URL and content type, and the client can then download the data from the URL, one download per :class:`.Blob` object.
130130

131131
When a :class:`.Blob` is serialized, the URL is generated with a unique ID to allow it to be downloaded. The URL is not guaranteed to be permanent, and should not be used as a long-term reference to the data. The URL will expire after 5 minutes, and the data will no longer be available for download after that time.
132132

docs/source/concurrency.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
Concurrency in `labthings-fastapi`
1+
Concurrency in LabThings-FastAPI
22
==================================
33

44
One of the major challenges when controlling hardware, particularly from web frameworks, is concurrency. Most web frameworks assume resources (database connections, object storage, etc.) may be instantiated multiple times, and often initialise or destroy objects as required. In contrast, hardware can usually only be controlled from one process, and usually is initialised and shut down only once.
55

6-
`labthings-fastapi` instantiates each `Thing` only once, and runs all code in a thread. More specifically, each time an action is invoked via HTTP, a new thread is created to run the action. Similarly, each time a property is read or written, a new thread is created to run the property method. This means that `Thing` code should protect important variables or resources using locks from the `threading` module, and need not worry about writing asynchronous code.
6+
LabThings-FastAPI instantiates each :class:`.Thing` only once, and runs all code in a thread. More specifically, each time an action is invoked via HTTP, a new thread is created to run the action. Similarly, each time a property is read or written, a new thread is created to run the property method. This means that :class:`.Thing` code should protect important variables or resources using locks from the `threading` module, and need not worry about writing asynchronous code.
77

8-
In the case of properties, the HTTP response is only returned once the `Thing` code is complete. Actions currently return a response immediately, and must be polled to determine when they have completed. This behaviour may change in the future, most likely with the introduction of a timeout to allow the client to choose between waiting for a response or polling.
8+
In the case of properties, the HTTP response is only returned once the :class:`.Thing` code is complete. Actions currently return a response immediately, and must be polled to determine when they have completed. This behaviour may change in the future, most likely with the introduction of a timeout to allow the client to choose between waiting for a response or polling.
99

10-
Many of the functions that handle HTTP requests are asynchronous, running in an `anyio` event loop. This enables many HTTP connections to be handled at once with good efficiency. The interface between async and threaded code is provided by a "Blocking Portal" created when the LabThings server is started. A FastAPI Dependency allows the blocking portal to be obtained: while it's very unlikely more than one LabThings server will exist in one Python instance, we avoid referring to the blocking portal globally in an effort to avoid concurrency issues.
10+
Many of the functions that handle HTTP requests are asynchronous, running in an :mod:`anyio` event loop. This enables many HTTP connections to be handled at once with good efficiency. The interface between async and threaded code is provided by a :class:`anyio.BlockingPortal`, created when the LabThings server is started. A FastAPI Dependency allows the blocking portal to be obtained: while it's very unlikely more than one LabThings server will exist in one Python instance, we avoid referring to the blocking portal globally in an effort to avoid concurrency issues.
1111

12-
If threaded code needs to call code in the `anyio` event loop, the blocking portal dependency should be used. There are relatively few occasions when `Thing` code will need to consider this explicitly: more usually the blocking portal will be obtained by a LabThings function, for example the `MJPEGStream` class.
12+
If threaded code needs to call code in the `anyio` event loop, the :class:`~.dependencies.blocking_portal.BlockingPortal` dependency should be used. There are relatively few occasions when :class:`.Thing` code will need to consider this explicitly: more usually the blocking portal will be obtained by a LabThings function, for example the :class:`.MJPEGStream` class.
1313

14-
When one `Thing` calls the actions or properties of another `Thing`, either directly or via a `DirectThingClient`, no new threads are spawned: the action or property is run in the same thread as the caller. This mirrors the behaviour of the `ThingClient`, which blocks until the action or property is complete.
14+
When one `Thing` calls the actions or properties of another :class:`.Thing`, either directly or via a :class:`.DirectThingClient`, no new threads are spawned: the action or property is run in the same thread as the caller. This mirrors the behaviour of the :class:`.ThingClient`, which blocks until the action or property is complete.
1515

docs/source/conf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
templates_path = ["_templates"]
2626
exclude_patterns = []
2727

28+
default_role = "py:obj"
29+
2830
autodoc2_packages = ["../../src/labthings_fastapi"]
2931
autodoc2_render_plugin = "myst"
3032
autodoc2_class_docstring = "both"
@@ -43,6 +45,8 @@
4345
intersphinx_mapping = {
4446
"python": ("https://docs.python.org/3", None),
4547
"fastapi": ("https://fastapi.tiangolo.com", None),
48+
"anyio": ("https://anyio.readthedocs.io/en/stable/", None),
49+
"pydantic": ("https://docs.pydantic.dev/latest/", None),
4650
}
4751

4852
myst_enable_extensions = ["fieldlist"]

docs/source/lt_core_concepts.rst

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,43 @@
11
LabThings Core Concepts
2-
=============
2+
=======================
33

44
LabThings FastAPI is a ground-up rewrite of LabThings using FastAPI. Many of the core concepts from FastAPI such as dependency injection are used heavily
55

66
The LabThings Server
77
--------------------
88

9-
At its core LabThings FastAPI is a server-based framework. To use LabThings FastAPI a LabThings Server is created, and `Thing`s are added to the the server to provide functionality.
9+
At its core LabThings FastAPI is a server-based framework. To use LabThings FastAPI a LabThings Server is created, and `.Thing` objects are added to the the server to provide functionality.
1010

1111
The server API is accessed over an HTTP requests, allowing client code (see below) to be written in any language that can send an HTTP request.
1212

1313
Client Code
1414
-----------
1515

16-
Clients or client code (Not to be confused with a ThingClient, see below) is the terminology used to describe any software that uses HTTP requests to access the LabThing Server. Clients 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.
16+
Clients or client code (Not to be confused with a :class:`.ThingClient`, see below) is the terminology used to describe any software that uses HTTP requests to access the LabThing Server. Clients 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.
1717

1818
Everything is a Thing
1919
---------------------
2020

21-
As described in :doc:`wot_core_concepts`, a `Thing` represents a piece of hardware or software. `labthings-fastapi` automatically generates a `Thing Description`_ to describe each `Thing`. Each function offered by the `Thing` is either a Property, Action, or Event. These are termed "interaction affordances" in WoT_ terminology.
21+
As described in :doc:`wot_core_concepts`, a Thing represents a piece of hardware or software. LabThings-FastAPI automatically generates a `Thing Description`_ to describe each Thing. Each function offered by the Thing is either a Property, Action, or Event. These are termed "interaction affordances" in WoT_ terminology.
2222

23-
Code on the LabThings FastAPI Server is composed of Things, however these can call generic Python functions/classes. The entire HTTP API served by the server is defined by `Thing`s. As such the full API is composed of the actions and properties (and perhaps eventually events) defined in each `Thing`.
23+
Code on the LabThings FastAPI Server is composed of Things, however these can call generic Python functions/classes. The entire HTTP API served by the server is defined by :class:`.Thing` objects. As such the full API is composed of the actions and properties (and perhaps eventually events) defined in each Thing.
24+
25+
_`Thing Description`: wot_core_concepts#thing
26+
_`WoT`: wot_core_concepts
2427

2528
ThingClients
2629
------------
2730

2831
When writing client code in Python it would be possible to formulate every interaction as an HTTP request. This has two major downsides:
2932

3033
1. The code must establish a new connection to the server for each request.
31-
2. Each request is formulated as a string pointing to the endpoint and `json` headers for sending any data. This leads to very messy code.
34+
2. Each request is formulated as a string pointing to the endpoint and ``json`` headers for sending any data. This leads to very messy code.
3235

3336
Ideally the client would be able to run the `Thing` object's actions and read its properties in native python code. However, as the client code is running in a different process, and probably in a different python environment (or even on a different machine entirely!) there is no way to directly import the Python objectfor the `Thing`.
3437

3538
To mitigate this client code can ask the server for a description of all of a `Thing`'s properties and actions, this is known as a `ThingDescription`. From this `ThingDescription` the client code can dynamically generate a new object with methods matching each `ThingAction` and properties matching each `ThingProperty`. **This dynamically generated object is called a ThingClient**.
3639

37-
The `ThingClient` also handle supplying certain arguments to ThingActions without them needing to be explicitly passed each time the method is called. More detail on this is provided in the :doc:`dependencies` page.
40+
The :class:`.ThingClient` also handle supplying certain arguments to ThingActions without them needing to be explicitly passed each time the method is called. More detail on this is provided in the :doc:`dependencies/dependencies` page.
3841

3942
DirectThingClients
4043
------------------

0 commit comments

Comments
 (0)