labthings_fastapi.descriptors.action
====================================
.. py:module:: labthings_fastapi.descriptors.action
.. autoapi-nested-parse::
Define an object to represent an Action, as a descriptor.
Attributes
----------
.. autoapisummary::
labthings_fastapi.descriptors.action.ACTION_POST_NOTICE
labthings_fastapi.descriptors.action.ACTION_GET_DESCRIPTION
Classes
-------
.. autoapisummary::
labthings_fastapi.descriptors.action.ActionDescriptor
Module Contents
---------------
.. py:data:: ACTION_POST_NOTICE
:value: Multiline-String
.. raw:: html
Show Value
.. code-block:: python
"""
## Important note
This `POST` request starts an Action, i.e. the server will do something
that may continue after the HTTP request has been responded to. The
response will always be an ActionInvocation object, that details the current
status of the action and provides an interface to poll for completion.
If the action completes within a specified timeout, we will return
an HTTP status code of `200` and the return value will include any
output from the action. If it does not complete, we will return a
`201` response code, and the action's endpoint may be polled to follow
its progress.
"""
.. raw:: html
.. py:data:: ACTION_GET_DESCRIPTION
:value: Multiline-String
.. raw:: html
Show Value
.. code-block:: python
"""
This will include times and input values, as well as output values for
actions that have completed. These actions will also show up under the
`action_invocations` endpoint, and can also be retrieved individually
using the link included in each action.
"""
.. raw:: html
.. py:class:: ActionDescriptor(func: Callable, response_timeout: float = 1, retention_time: float = 300)
Wrap actions to enable them to be run over HTTP.
This class is responsible for generating the action description for
the :ref:`wot_td` and creating the function that responds to ``POST``
requests to invoke the action.
.. note::
Descriptors are instantiated once per class. This means that we cannot
assume there is only one action corresponding to this descriptor: there
may be multiple `.Thing` instances with the same descriptor. That is
why the host `.Thing` must be passed to many functions as an argument,
and why observers, for example, must be keyed by the `.Thing` rather
than kept as a property of ``self``.
Create a new action descriptor.
The action descriptor wraps a method of a `.Thing`. It may still be
called from Python in the same way, but it will also be added to the
HTTP API and automatic documentation.
:param func: is the method that will be run when the action is called.
:param response_timeout: is how long we should wait before returning a
response to the client. This is not currently used, as we always
return immediately with a `201` code. In the future, it may set a
default time to wait before responding. If the action finishes
before we respond, we will be able to return the completed action
and its output. If the action is still running, we return a 201
code and data enabling the client to poll to find out the status
of the action.
:param retention_time: how long, in seconds, the action should be kept
for after it has completed.
.. py:attribute:: func
.. py:attribute:: response_timeout
:value: 1
.. py:attribute:: retention_time
:value: 300
.. py:attribute:: dependency_params
:value: []
.. py:attribute:: input_model
.. py:attribute:: output_model
.. py:attribute:: invocation_model
:value: None
.. py:method:: __get__(obj: Literal[None], type=None) -> ActionDescriptor
__get__(obj: labthings_fastapi.thing.Thing, type=None) -> Callable
Return the function, bound to an object as for a normal method.
This currently doesn't validate the arguments, though it may do so
in future. In its present form, this is equivalent to a regular
Python method, i.e. all we do is supply the first argument, `self`.
If `obj` is None, the descriptor is returned, so we can get
the descriptor conveniently as an attribute of the class.
:param obj: the `.Thing` to which we are attached. This will be
the first argument supplied to the function wrapped by this
descriptor.
:param type: the class of the `.Thing` to which we are attached.
If the descriptor is accessed via the class it is returned
directly.
:return: the action function, bound to ``obj`` (when accessed
via an instance), or the descriptor (accessed via the class).
.. py:property:: name
The name of the wrapped function.
.. py:property:: title
A human-readable title.
.. py:property:: description
A description of the action.
.. py:method:: _observers_set(obj: labthings_fastapi.thing.Thing) -> weakref.WeakSet
Return a set used to notify changes.
Note that we need to supply the `.Thing` we are looking at, as in
general there may be more than one object of the same type, and
descriptor instances are shared between all instances of their class.
:param obj: The `.Thing` on which the action is being observed.
:return: a weak set of callables to notify on changes to the action.
This is used by websocket endpoints.
.. py:method:: emit_changed_event(obj: labthings_fastapi.thing.Thing, status: str)
Notify subscribers that the action status has changed.
This function is run from within the `.Invocation` thread that
is created when an action is called. It must be run from a thread
as it is communicating with the event loop via an `asyncio` blocking
portal. Async code must not use the blocking portal as it can deadlock
the event loop.
:param obj: The `.Thing` on which the action is being observed.
:param status: The status of the action, to be sent to observers.
:raise NotConnectedToServerError: if the Thing calling the action is not
connected to a server with a running event loop.
.. py:method:: emit_changed_event_async(obj: labthings_fastapi.thing.Thing, value: Any) -> None
:async:
Notify subscribers that the action status has changed.
This is an async function that must be run in the `anyio` event loop.
It will send messages to each observer to notify them that something
has changed.
:param obj: The `.Thing` on which the action is defined.
`.ActionDescriptor` objects are unique to the class, but there may
be more than one `.Thing` attached to a server with the same class.
We use ``obj`` to look up the observers of the current `.Thing`.
:param value: The action status to communicate to the observers.
.. py:method:: add_to_fastapi(app: fastapi.FastAPI, thing: labthings_fastapi.thing.Thing) -> None
Add this action to a FastAPI app, bound to a particular Thing.
This function creates two functions to handle ``GET`` and ``POST``
requests to the action's endpoint, and adds them to the `fastapi.FastAPI`
application.
:param app: The `fastapi.FastAPI` app to add the endpoint to.
:param thing: The `.Thing` to which the action is attached. Bear in
mind that the descriptor may be used by more than one `.Thing`,
so this can't be a property of the descriptor.
.. py:method:: action_affordance(thing: labthings_fastapi.thing.Thing, path: Optional[str] = None) -> labthings_fastapi.thing_description._model.ActionAffordance
Represent the property in a Thing Description.
This function describes the Action in :ref:`wot_td` format.
:param thing: The `.Thing` to which the action is attached.
:param path: The prefix applied to all endpoints associated with the
`.Thing`. This is the URL for the Thing Description. If it is
omitted, we use the ``path`` property of the ``thing``.
:return: An `.ActionAffordance` describing this action.