labthings_fastapi

LabThings-FastAPI.

This is the top level module for LabThings-FastAPI, a library for building Web of Things Core Concepts devices using Python. There is documentation on readthedocs, and the recommended place to start is labthings_fastapi.

This module contains a number of convenience imports and is intended to be imported using:

import labthings_fastapi as lt

The example code elsewhere in the documentation generally follows this convention. Symbols in the top-level module mostly exist elsewhere in the package, but should be imported from here as a preference, to ensure code does not break if modules are rearranged.

Submodules

Classes

Thing

Represents a Thing, as defined by the Web of Things standard.

ThingProperty

A property that can be accessed via the HTTP API.

ThingSetting

A ThingProperty that persists on disk.

ThingServer

Use FastAPI to serve Thing instances.

ThingClient

A client for a LabThings-FastAPI Thing.

Functions

thing_property(...)

Mark a method of a Thing as a LabThings Property.

thing_setting(→ labthings_fastapi.descriptors.ThingSetting)

Mark a method of a Thing as a LabThings Setting.

thing_action(…)

Mark a method of a Thing as a LabThings Action.

fastapi_endpoint(→ Callable[[Callable], ...)

Mark a function as a FastAPI endpoint without making it an action.

get_blocking_portal(...)

Retrieve a blocking portal from a Thing.

Package Contents

class labthings_fastapi.Thing

Represents a Thing, as defined by the Web of Things standard.

This class should encapsulate the code that runs a piece of hardware, or provides a particular function - it will correspond to a path on the server, and a Thing Description document.

Subclassing Notes

  • __init__: You should accept any arguments you need to configure the Thing

    in __init__. Don’t initialise any hardware at this time, as your Thing may be instantiated quite early, or even at import time.

  • __enter__(self) and __exit__(self, exc_t, exc_v, exc_tb) are where you

    should start and stop communications with the hardware. This is Python’s “context manager” protocol. The arguments of __exit__ will be None except after errors. You should be safe to ignore them, and just include code that will close down your hardware, which is equivalent to a finally: block.

  • Properties and Actions are defined using decorators: the :deco:`.thing_action`

    decorator declares a method to be an action, which will run when it’s triggered, and the :deco:`.thing_property` decorator (or ThingProperty descriptor) does the same for a property. See the documentation on those functions for more detail.

  • title will be used in various places as the human-readable name of your Thing,

    so it makes sense to set this in a subclass.

There are various LabThings methods that you should avoid overriding unless you know what you are doing: anything not mentioned above that’s defined in Thing is probably best left alone. They may in time be collected together into a single object to avoid namespace clashes.

title: str

A human-readable description of the Thing

_labthings_blocking_portal: anyio.from_thread.BlockingPortal | None = None

See Concurrency in LabThings-FastAPI for why blocking portal is needed.

path: str | None

The path at which the Thing is exposed over HTTP.

async __aenter__() typing_extensions.Self

Context management is used to set up/close the thing.

As things (currently) do everything with threaded code, we define async __aenter__ and __aexit__ wrappers to call the synchronous code, if it exists.

Returns:

this object.

async __aexit__(exc_t: Any | None, exc_v: Any | None, exc_tb: Any) None

Wrap context management functions, if they exist.

See __aenter__ for more details.

Parameters:
  • exc_t – The type of the exception, or None.

  • exc_v – The exception that occurred, or None.

  • exc_tb – The traceback for the exception, or None.

attach_to_server(server: labthings_fastapi.server.ThingServer, path: str, setting_storage_path: str) None

Attach this thing to the server.

Things need to be attached to a server before use to function correctly.

Parameters:
  • server – The server to attach this Thing to.

  • path – The root URL for the Thing.

  • setting_storage_path – The path on disk to save the any Thing Settings to. This should be the path to a json file. If it does not exist it will be created.

Attaching the Thing to a ThingServer allows the Thing to start actions, load its settings from the correct place, and create HTTP endpoints to allow it to be accessed from the HTTP API.

We create HTTP endpoints for all Interaction Affordances on the Thing, as well as any EndpointDescriptor descriptors.

_settings_store: dict[str, labthings_fastapi.descriptors.ThingSetting] | None = None
property _settings: dict[str, labthings_fastapi.descriptors.ThingSetting]

A private property that returns a dict of all settings for this Thing.

Each dict key is the name of the setting, the corresponding value is the ThingSetting class (a descriptor). This can be used to directly get the descriptor so that the value can be set without emitting signals, such as on startup.

_setting_storage_path: str | None = None
property setting_storage_path: str | None

The storage path for settings.

Note

This is set in Thing.attach_to_server. It is None during the __init__ method, so it is best to avoid using settings until the Thing is set up in __enter__.

load_settings(setting_storage_path: str) None

Load settings from json.

Read the JSON file and use it to populate settings.

Note

Settings are loaded when the Thing is added to a server, so they will not be available while the __init__ method is run.

Note that no notifications will be triggered when the settings are set, so if action is needed (e.g. updating hardware with the loaded settings) it should be taken in __enter__.

Parameters:

setting_storage_path – The path where the settings should be stored.

save_settings()

Save settings to JSON.

This is called whenever a setting is updated. All settings are written to the settings file every time.

_labthings_thing_state: dict | None = None
property thing_state: collections.abc.Mapping

Return a dictionary summarising our current state.

This is intended to be an easy way to collect metadata from a Thing that summarises its state. It might be used, for example, to record metadata along with each reading/image/etc. when an instrument is saving data.

It’s best to populate this automatically so it can always be accessed. If it requires calls e.g. to a serial instrument, bear in mind it may be called quite often and shouldn’t take too long.

Some measure of caching here is a nice aim for the future, but not yet implemented.

validate_thing_description() None

Raise an exception if the thing description is not valid.

_cached_thing_description: tuple[str | None, str | None, labthings_fastapi.thing_description._model.ThingDescription] | None = None
thing_description(path: str | None = None, base: str | None = None) labthings_fastapi.thing_description._model.ThingDescription

Generate a w3c Thing Description representing this thing.

The w3c Web of Things working group defined a standard representation of a Thing, which provides a high-level description of the actions, properties, and events that it exposes. This endpoint delivers a JSON representation of the Thing Description for this Thing.

Parameters:
  • path – the URL pointing to this Thing.

  • base – the base URL for all URLs in the thing description.

Returns:

a Thing Description.

thing_description_dict(path: str | None = None, base: str | None = None) dict

Describe this Thing with a Thing Description as a simple dict.

See Thing.thing_description. This function converts the return value of that function into a simple dictionary.

Parameters:
  • path – the URL pointing to this Thing.

  • base – the base URL for all URLs in the thing description.

Returns:

a Thing Description.

observe_property(property_name: str, stream: anyio.abc.ObjectSendStream) None

Register a stream to receive property change notifications.

Parameters:
  • property_name – the property to register for.

  • stream – the stream used to send events.

Raises:

KeyError – if the requested name is not defined on this Thing.

observe_action(action_name: str, stream: anyio.abc.ObjectSendStream)

Register a stream to receive action status change notifications.

Parameters:
  • action_name – the action to register for.

  • stream – the stream used to send events.

Raises:

KeyError – if the requested name is not defined on this Thing.

class labthings_fastapi.ThingProperty(model: type, initial_value: Any = None, readonly: bool = False, observable: bool = False, description: str | None = None, title: str | None = None, getter: Callable | None = None, setter: Callable | None = None)

A property that can be accessed via the HTTP API.

By default, a ThingProperty acts like a normal variable, but functionality can be added in several ways.

Create a property that can be accessed via the HTTP API.

ThingProperty is a descriptor that functions like a variable, optionally with notifications when it is set. It may also have a getter and setter, which work in a similar way to Python properties.

ThingProperty can behave in several different ways:

  • If no getter or setter is specified, it will behave like a simple data attribute (i.e. a variable). If observable is True, it is possible to register for notifications when the value is set. In this case, an initial_value is required.

  • If a getter is specified and observable is False, the getter will be called when the property is accessed, and its return value will be the property’s value, just like the builtin property. The property will be read-only both locally and via HTTP.

  • If a getter is specified and observable is True, the getter is used instead of initial_value but thereafter the property behaves like a variable. The getter is only on first access. The property may be written to locally, and whether it’s writable via HTTP depends on the readonly argument.

  • If both a getter and setter are specified and observable is False, the property behaves like a Python property, with the getter being called when the property is accessed, and the setter being called when the property is set. The property is read-only via HTTP if readonly is True. It may always be written to locally.

  • If observable is True and a setter is specified, the property will behave like a variable, but will call the setter when the property is set. The setter may perform tasks like sending the updated value to the hardware, but it is not responsible for remembering the value. The initial value is set via the getter or initial_value.

Parameters:
  • model – The type of the property. This is optional, because it is better to use type hints (see notes on typing above).

  • initial_value – The initial value of the property. If this is set, the property must not have a getter, and should behave like a variable.

  • readonly – If True, the property cannot be set via the HTTP API.

  • observable – If True, the property can be observed for changes via websockets. This causes the setter to run code in the async event loop that will notify a list of subscribers each time the property is set. Currently, only websockets can be used to observe properties.

  • description – A description of the property, used in the API documentation. LabThings will attempt to take this from the docstring if not supplied.

  • title – A human-readable title for the property, used in the API documentation. Defaults to the first line of the docstring, or the name of the property.

  • getter – A function that gets the value of the property.

  • setter – A function that sets the value of the property.

Raises:

ValueError – if the initial value or type are missing or incorrectly specified.

model: type[pydantic.BaseModel]
readonly: bool = False
observable = False
initial_value = None
_description = None
_title = None
_setter
_getter
__set_name__(owner: type[labthings_fastapi.thing.Thing], name: str) None

Take note of the name to which the descriptor is assigned.

This is called when the descriptor is assigned to an attribute of a class.

Parameters:
  • owner – the Thing subclass to which we are being attached.

  • name – the name to which we have been assigned.

property title

A human-readable title for the property.

property description

A description of the property.

__get__(obj: labthings_fastapi.thing.Thing | None, type: ThingProperty.__get__.type | None = None) Any

Return the value of the property.

If obj is none (i.e. we are getting the attribute of the class), we return the descriptor.

If no getter is set, we’ll return either the initial value, or the value from the object’s __dict__, i.e. we behave like a variable.

If a getter is set, we will use it, unless the property is observable, at which point the getter is only ever used once, to set the initial value.

Parameters:
  • obj – the Thing to which we are attached.

  • type – the class on which we are defined.

Returns:

the value of the property (when accessed on an instance), or this descriptor if accessed as a class attribute.

__set__(obj: labthings_fastapi.thing.Thing, value: Any) None

Set the property’s value.

Parameters:
  • obj – the Thing to which we are attached.

  • value – the new value for the property.

_observers_set(obj: labthings_fastapi.thing.Thing)

Return the observers of this property.

Each observer in this set will be notified when the property is changed. See .ThingProperty.emit_changed_event

Parameters:

obj – the Thing to which we are attached.

Returns:

the set of observers corresponding to obj.

emit_changed_event(obj: labthings_fastapi.thing.Thing, value: Any) None

Notify subscribers that the property has changed.

This function is run when properties are updated. It must be run from within a thread. This could be the Invocation thread of a running action, or the property should be updated over via a client/http. It must be run from a thread as it is communicating with the event loop via an asyncio blocking portal and can cause deadlock if run in the event loop.

Parameters:
  • obj – the Thing to which we are attached.

  • value – the new property value, to be sent to observers.

Raises:

NotConnectedToServerError – if the Thing that is calling the property update is not connected to a server with a running event loop.

async emit_changed_event_async(obj: labthings_fastapi.thing.Thing, value: Any)

Notify subscribers that the property has changed.

This function may only be run in the anyio event loop. See ThingProperty.emit_changed_event.

Parameters:
  • obj – the Thing to which we are attached.

  • value – the new property value, to be sent to observers.

property name

The name of the property.

This should be consistent between the class definition and the Thing Description as well as appearing in the URLs for getting and setting.

add_to_fastapi(app: fastapi.FastAPI, thing: labthings_fastapi.thing.Thing) None

Add this action to a FastAPI app, bound to a particular Thing.

Parameters:
  • app – The FastAPI application we are adding endpoints to.

  • thing – The Thing we are adding the endpoints for.

property_affordance(thing: labthings_fastapi.thing.Thing, path: str | None = None) labthings_fastapi.thing_description._model.PropertyAffordance

Represent the property in a Thing Description.

Parameters:
  • thing – the Thing to which we are attached.

  • path – the URL of the Thing. If not present, we will retrieve the path from thing.

Returns:

A description of the property in Thing Description format.

getter(func: Callable) typing_extensions.Self

Set the function that gets the property’s value.

Parameters:

func – is the new getter function.

Returns:

this property (to allow its use as a decorator).

setter(func: Callable) typing_extensions.Self

Change the setter function.

ThingProperty descriptors return the value they hold when they are accessed. However, they can run code when they are set: this decorator sets a function as that code.

Parameters:

func – is the new setter function.

Returns:

this property (to allow its use as a decorator).

class labthings_fastapi.ThingSetting(model: type, initial_value: Any = None, readonly: bool = False, observable: bool = False, description: str | None = None, title: str | None = None, getter: Callable | None = None, setter: Callable | None = None)

Bases: ThingProperty

A ThingProperty that persists on disk.

A setting can be accessed via the HTTP API and is persistent between sessions.

A ThingSetting is a ThingProperty with extra functionality for triggering a Thing to save its settings.

Note: If a setting is mutated rather than assigned to, this will not trigger saving. For example: if a Thing has a setting called dictsetting holding the dictionary {"a": 1, "b": 2} then self.dictsetting = {"a": 2, "b": 2} would trigger saving but self.dictsetting[a] = 2 would not, as the setter for dictsetting is never called.

The setting otherwise acts just like a normal variable.

Create a property that can be accessed via the HTTP API.

ThingProperty is a descriptor that functions like a variable, optionally with notifications when it is set. It may also have a getter and setter, which work in a similar way to Python properties.

ThingProperty can behave in several different ways:

  • If no getter or setter is specified, it will behave like a simple data attribute (i.e. a variable). If observable is True, it is possible to register for notifications when the value is set. In this case, an initial_value is required.

  • If a getter is specified and observable is False, the getter will be called when the property is accessed, and its return value will be the property’s value, just like the builtin property. The property will be read-only both locally and via HTTP.

  • If a getter is specified and observable is True, the getter is used instead of initial_value but thereafter the property behaves like a variable. The getter is only on first access. The property may be written to locally, and whether it’s writable via HTTP depends on the readonly argument.

  • If both a getter and setter are specified and observable is False, the property behaves like a Python property, with the getter being called when the property is accessed, and the setter being called when the property is set. The property is read-only via HTTP if readonly is True. It may always be written to locally.

  • If observable is True and a setter is specified, the property will behave like a variable, but will call the setter when the property is set. The setter may perform tasks like sending the updated value to the hardware, but it is not responsible for remembering the value. The initial value is set via the getter or initial_value.

Parameters:
  • model – The type of the property. This is optional, because it is better to use type hints (see notes on typing above).

  • initial_value – The initial value of the property. If this is set, the property must not have a getter, and should behave like a variable.

  • readonly – If True, the property cannot be set via the HTTP API.

  • observable – If True, the property can be observed for changes via websockets. This causes the setter to run code in the async event loop that will notify a list of subscribers each time the property is set. Currently, only websockets can be used to observe properties.

  • description – A description of the property, used in the API documentation. LabThings will attempt to take this from the docstring if not supplied.

  • title – A human-readable title for the property, used in the API documentation. Defaults to the first line of the docstring, or the name of the property.

  • getter – A function that gets the value of the property.

  • setter – A function that sets the value of the property.

Raises:

ValueError – if the initial value or type are missing or incorrectly specified.

__set__(obj: labthings_fastapi.thing.Thing, value: Any)

Set the setting’s value.

This will cause the settings to be saved to disk.

Parameters:
  • obj – the Thing to which we are attached.

  • value – the new value of the setting.

set_without_emit(obj: labthings_fastapi.thing.Thing, value: Any)

Set the property’s value, but do not emit event to notify the server.

This function is not expected to be used externally. It is called during initial setup so that the setting can be set from disk before the server is fully started.

Parameters:
  • obj – the Thing to which we are attached.

  • value – the new value of the setting.

labthings_fastapi.thing_property(func: Callable) labthings_fastapi.descriptors.ThingProperty

Mark a method of a Thing as a LabThings Property.

This should be used as a decorator with a getter and a setter just like a standard python property decorator. If extra functionality is not required in the decorator, then using the ThingProperty class directly may allow for clearer code

Properties should always have a type annotation. This type annotation will be used in automatic documentation and also to serialise the value to JSON when it is sent over th network. This mean that the type of your property should either be JSON serialisable (i.e. simple built-in types) or a subclass of pydantic.BaseModel.

Parameters:

func – A method to use as the getter for the new property.

Returns:

A ThingProperty descriptor that works like property but allows the value to be read over HTTP.

labthings_fastapi.thing_setting(func: Callable) labthings_fastapi.descriptors.ThingSetting

Mark a method of a Thing as a LabThings Setting.

A setting is a property that is saved to disk, so it persists even when the LabThings server is restarted.

This should be used as a decorator with a getter and a setter just like a standard python property decorator. If extra functionality is not required in the decorator, then using the ThingSetting class directly may allow for clearer code where the property works like a variable.

When creating a setting using this decorator, you must always add a setter as it is used to load the value from disk. This follows the same syntax as for property, i.e. a second function with the same name, decorated with @my_property_name.setter.

A type annotation is required, and should follow the same constraints as for :deco:`thing_property`.

If the type is a pydantic BaseModel, then the setter must also be able to accept the dictionary representation of this BaseModel as this is what will be used to set the Setting when loading from disk on starting the server.

Note

If a setting is mutated rather than set, this will not trigger saving. For example: if a Thing has a setting called dictsetting holding the dictionary {"a": 1, "b": 2} then self.dictsetting = {"a": 2, "b": 2} would trigger saving but self.dictsetting[a] = 2 would not, as the setter for dictsetting is never called.

Parameters:

func – A method to use as the getter for the new property.

Returns:

A ThingSetting descriptor that works like property but allows the value to be read over HTTP and saves it to disk.

labthings_fastapi.thing_action(func: Callable, **kwargs) labthings_fastapi.descriptors.ActionDescriptor
labthings_fastapi.thing_action(**kwargs) Callable[[Callable], labthings_fastapi.descriptors.ActionDescriptor]

Mark a method of a Thing as a LabThings Action.

Methods decorated with :deco:`thing_action` will be available to call over HTTP as actions. See Actions for an introduction to the concept of actions.

This decorator may be used with or without arguments.

Parameters:
  • func – The method to be decorated as an action.

  • **kwargs – Keyword arguments are passed to the constructor of ActionDescriptor.

Returns:

Whether used with or without arguments, the result is that the method is wrapped in an ActionDescriptor, so it can be called as usual, but will also be exposed over HTTP.

labthings_fastapi.fastapi_endpoint(method: labthings_fastapi.descriptors.HTTPMethod, path: str | None = None, **kwargs) Callable[[Callable], labthings_fastapi.descriptors.EndpointDescriptor]

Mark a function as a FastAPI endpoint without making it an action.

This decorator will cause a method of a Thing to be directly added to the HTTP API, bypassing the machinery underlying Action and Property affordances. Such endpoints will not be documented in the Thing Description but may be used as the target of links. For example, this could allow a file to be downloaded from the Thing at a known URL, or serve a video stream that wouldn’t be supported as a Blob.

The majority of Thing implementations won’t need this decorator, but it is here to enable flexibility when it’s needed.

This decorator always takes arguments; in particular, method is required. It should be used as:

class DownloadThing(Thing):
    @fastapi_endpoint("get")
    def plain_text_response(self) -> str:
        return "example string"

This decorator is intended to work very similarly to the fastapi decorators @app.get, @app.post, etc., with two changes:

  1. The path is relative to the host Thing and will default to the name

    of the method.

  2. The method will be called with the host Thing as its first argument,

    i.e. it will be bound to the class as usua.

Parameters:
  • method – The HTTP verb this endpoint responds to.

  • path – The path, relative to the host Thing base URL.

  • **kwargs – Additional keyword arguments are passed to the fastapi.FastAPI.get decorator if method is get, or to the equivalent decorator for other HTTP verbs.

Returns:

When used as intended, the result is an EndpointDescriptor.

class labthings_fastapi.ThingServer(settings_folder: str | None = None)

Use FastAPI to serve Thing instances.

The ThingServer sets up a fastapi.FastAPI application and uses it to expose the capabilities of Thing instances over HTTP.

There are several functions of a ThingServer:

  • Manage where settings are stored, to allow Thing instances to load and save their settings from disk.

  • Configure the server to allow cross-origin requests (required if we use a web app that is not served from the ThingServer).

  • Manage the threads used to run Actions.

  • Manage Blob input/output to allow binary data to be returned.

  • Allow threaded code to call functions in the event loop, by providing an anyio.from_thread.BlockingPortal.

Initialise a LabThings server.

Setting up the ThingServer involves creating the underlying fastapi.FastAPI app, setting its lifespan function (used to set up and shut down the Thing instances), and configuring it to allow cross-origin requests.

We also create the ActionManager to manage Actions and the BlobManager to manage the downloading of Blob input/output.

Parameters:

settings_folder – the location on disk where Thing settings will be saved.

app
settings_folder = './settings'
action_manager
blob_data_manager
_things: dict[str, labthings_fastapi.thing.Thing]
blocking_portal: anyio.from_thread.BlockingPortal | None = None
startup_status: dict[str, str | dict]
set_cors_middleware() None

Configure the server to allow requests from other origins.

This is required to allow web applications access to the HTTP API, if they are not served from the same origin (i.e. if they are not served as part of the ThingServer.).

This is usually needed during development, and may be needed at other times depending on how you are using LabThings.

property things: collections.abc.Mapping[str, labthings_fastapi.thing.Thing]

Return a dictionary of all the things.

Returns:

a dictionary mapping thing paths to Thing instances.

ThingInstance
things_by_class(cls: type[ThingInstance]) Sequence[ThingInstance]

Return all Things attached to this server matching a class.

Return all instances of cls attached to this server.

Parameters:

cls – A Thing subclass.

Returns:

all instances of cls that have been added to this server.

thing_by_class(cls: type[ThingInstance]) ThingInstance

Return the instance of cls attached to this server.

This function calls ThingServer.things_by_class, but asserts that there is exactly one match.

Parameters:

cls – a Thing subclass.

Returns:

the instance of cls attached to this server.

Raises:

RuntimeError – if there is not exactly one matching Thing.

add_thing(thing: labthings_fastapi.thing.Thing, path: str) None

Add a thing to the server.

Parameters:
  • thing – The Thing instance to add to the server.

  • path – the relative path to access the thing on the server. Must only contain alphanumeric characters, hyphens, or underscores.

Raises:
async lifespan(app: fastapi.FastAPI) AsyncGenerator[None]

Manage set up and tear down of the server and Things.

This method is used as a lifespan function for the FastAPI app. See the lifespan page in FastAPI’s documentation.

This does two important things:

  • It sets up the blocking portal so background threads can run async code (this is required for events, streams, etc.).

  • It runs setup/teardown code for Things by calling them as context managers.

Parameters:

app – The FastAPI application wrapped by the server.

Yield:

no value. The FastAPI application will serve requests while this function yields.

add_things_view_to_app()

Add an endpoint that shows the list of attached things.

class labthings_fastapi.ThingClient(base_url: str, client: httpx.Client | None = None)

A client for a LabThings-FastAPI Thing.

Note

ThingClient must be subclassed to add actions/properties, so this class will be minimally useful on its own.

The best way to get a client for a particular Thing is currently ThingClient.from_url, which dynamically creates a subclass with the right attributes.

Create a ThingClient connected to a remote Thing.

Parameters:
  • base_url – the base URL of the Thing. This should be the URL of the Thing Description document.

  • client – an optional httpx.Client object to use for all HTTP requests. This may be a fastapi.TestClient object for testing purposes.

server
path
client
get_property(path: str) Any

Make a GET request to retrieve the value of a property.

Parameters:

path – the URI of the getproperty endpoint, relative to the base_url.

Returns:

the property’s value, as deserialised from JSON.

set_property(path: str, value: Any)

Make a PUT request to set the value of a property.

Parameters:
  • path – the URI of the getproperty endpoint, relative to the base_url.

  • value – the property’s value. Currently this must be serialisable to JSON.

invoke_action(path: str, **kwargs)

Invoke an action on the Thing.

This method will make the initial POST request to invoke an action, then poll the resulting invocation until it completes. If successful, the action’s output will be returned directly.

While the action is running, log messages will be re-logged locally. If you have enabled logging to the console, these should be visible.

Parameters:
  • path – the URI of the invokeaction endpoint, relative to the base_url

  • **kwargs – Additional arguments will be combined into the JSON body of the POST request and sent as input to the action. These will be validated on the server.

Returns:

the output value of the action.

Raises:

RuntimeError – is raised if the action does not complete successfully.

Follow a link in a response object, by its rel attribute.

Parameters:
  • response – is the dictionary returned by e.g. poll_invocation.

  • rel – picks the link to follow by matching its rel item.

Returns:

the response to making a GET request to the link.

classmethod from_url(thing_url: str, client: httpx.Client | None = None) typing_extensions.Self

Create a ThingClient from a URL.

This will dynamically create a subclass with properties and actions, and return an instance of that subclass pointing at the Thing URL.

Parameters:
  • thing_url – The base URL of the Thing, which should also be the URL of its Thing Description.

  • client – is an optional httpx.Client object. If not present, one will be created. This is particularly useful if you need to set HTTP options, or if you want to work with a local server object for testing purposes (see fastapi.TestClient).

Returns:

a ThingClient subclass with properties and methods that match the retrieved Thing Description (see Thing).

classmethod subclass_from_td(thing_description: dict) type[typing_extensions.Self]

Create a ThingClient subclass from a Thing Description.

Dynamically subclass ThingClient to add properties and methods for each property and action in the Thing Description.

Parameters:

thing_description – A Thing Description as a dictionary, which will be used to construct the class.

Returns:

a ThingClient subclass with the right properties and methods.

labthings_fastapi.get_blocking_portal(obj: labthings_fastapi.thing.Thing) anyio.from_thread.BlockingPortal | None

Retrieve a blocking portal from a Thing.

See Concurrency in LabThings-FastAPI for more details.

When a Thing is attached to a ThingServer and the ThingServer is started, it sets an attribute on each Thing to allow it to access an anyio.from_thread.BlockingPortal. This allows threaded code to call async code.

This function retrieves the blocking portal from a Thing.

Parameters:

obj – the Thing on which we are looking for the portal.

Returns:

the blocking portal.