labthings_fastapi.thing_server_interface ======================================== .. py:module:: labthings_fastapi.thing_server_interface .. autoapi-nested-parse:: Interface between `~lt.Thing` subclasses and the `~lt.ThingServer`\ . Attributes ---------- .. autoapisummary:: labthings_fastapi.thing_server_interface.Params labthings_fastapi.thing_server_interface.ReturnType Exceptions ---------- .. autoapisummary:: labthings_fastapi.thing_server_interface.ThingServerMissingError Classes ------- .. autoapisummary:: labthings_fastapi.thing_server_interface.ThingServerInterface Module Contents --------------- .. py:data:: Params .. py:data:: ReturnType .. py:exception:: ThingServerMissingError Bases: :py:obj:`RuntimeError` The error raised when a ThingServer is no longer available. This error indicates that a ThingServerInterface is still in use even though its underlying ThingServer has been deleted. This is unlikely to happen and usually indicates that the server has been created in an odd way. Initialize self. See help(type(self)) for accurate signature. .. py:class:: ThingServerInterface(server: labthings_fastapi.server.ThingServer, name: str) An interface for Things to interact with their server. This is added to every `~lt.Thing` during ``__init__`` and is available as ``self._thing_server_interface``\ . Initialise a ThingServerInterface. The ThingServerInterface sits between a Thing and its ThingServer, with the intention of providing a useful set of functions, without exposing too much of the server to the Thing. One reason for using this intermediary class is to make it easier to mock the server during testing: only functions provided here need be mocked, not the whole functionality of the server. :param server: the `~lt.ThingServer` instance we're connected to. This will be retained as a weak reference. :param name: the name of the `~lt.Thing` instance this interface is provided for. .. py:attribute:: _name :type: str .. py:attribute:: _server :type: weakref.ReferenceType[labthings_fastapi.server.ThingServer] .. py:method:: _get_server() -> labthings_fastapi.server.ThingServer Return a live reference to the ThingServer. This will evaluate the weak reference to the ThingServer, and will raise an exception if the server has been garbage collected. The server is, in practice, not going to be finalized before the Things, so this should not be a problem. :returns: the ThingServer. :raises ThingServerMissingError: if the `ThingServer` is no longer available. .. py:method:: start_async_task_soon(async_function: Callable[Params, Awaitable[ReturnType]], *args: Any) -> concurrent.futures.Future[ReturnType] Run an asynchronous task in the server's event loop. This function wraps `anyio.from_thread.BlockingPortal.start_task_soon` to provide a way of calling asynchronous code from threaded code. It will call the provided async function in the server's event loop, without any guarantee of exactly when it will happen. This means we will return immediately, and the return value of this function will be a `concurrent.futures.Future` object that may resolve to the async function's return value. :param async_function: the asynchronous function to call. :param \*args: positional arguments to be provided to the function. :returns: an `asyncio.Future` object wrapping the return value. :raises ServerNotRunningError: if the server is not running (i.e. there is no event loop). .. py:method:: call_async_task(async_function: Callable[Params, Awaitable[ReturnType]], *args: Any) -> ReturnType Run an asynchronous task in the server's event loop in a blocking manner. This function wraps `anyio.from_thread.BlockingPortal.call` to provide a way of calling asynchronous code from threaded code. It will block the current thread while it calls the provided async function in the server's event loop. Do not call this from the event loop or it may lead to a deadlock. :param async_function: the asynchronous function to call. :param \*args: positional arguments to be provided to the function. :returns: The return value from the asynchronous function. :raises ServerNotRunningError: if the server is not running (i.e. there is no event loop). .. py:property:: settings_folder :type: str The path to a folder where persistent files may be saved. .. py:property:: settings_file_path :type: str The path where settings should be loaded and saved as JSON. .. py:property:: name :type: str The name of the Thing attached to this interface. .. py:property:: path :type: str The path, relative to the server's base URL, of the Thing. A ThingServerInterface is specific to one Thing, so this path points to the base URL of the Thing, i.e. the Thing Description's endpoint. .. py:property:: application_config :type: Mapping[str, Any] | None The custom application configuration options from configuration. .. py:method:: get_thing_states() -> Mapping[str, Any] Retrieve metadata from all Things on the server. This function will retrieve the `~lt.Thing.thing_state` property from each `~lt.Thing` on the server, and return it as a dictionary. It is intended to make it easy to add metadata to the results of actions, for example to embed in an image. :return: a dictionary of metadata, with the `~lt.Thing` names as keys. .. py:property:: _action_manager :type: labthings_fastapi.actions.ActionManager The ActionManager for the Thing attached to this interface. This property may be removed in future, and is for internal use only. .. py:property:: global_lock :type: labthings_fastapi.global_lock.GlobalLock | None A lock that ensures property writes and actions are one-at-a-time. If global locking is not enabled, this property will return None. .. py:method:: _optionally_hold_global_lock(enabled: bool | None = True) -> collections.abc.Iterator[None] Hold the global lock, if required, as a context manager. This function will hold the global lock if necessary while a block of code runs. Its behaviour is controlled by the `enabled` parameter: if `enabled` is `False` this function does nothing. If it is `None` (the default when called from a property or action that's not otherwise configured), the global lock is held if it exists, but no error is raised if global locking is disabled. If ``enabled`` is `True` (the default if no arguments are passed), an error will be raised if there is no global lock. :param enabled: whether to use the global lock. `True` and `False` have the obvious meanings described above, `None` will use the lock if it is enabled globally but won't raise an error if it is unavailable. :raises FeatureNotEnabledError: if `enabled` is `True` but the global lock is not enabled. .. py:method:: hold_global_lock(*, error_if_unavailable: bool = True) -> collections.abc.Iterator[None] Hold the global lock for the duration of a with block. This context manager will hold the global lock while a ``with:`` block runs. By default, an exception will be raised if the global lock is not enabled. :param error_if_unavailable: may be set to `False` to suppress errors if the global lock is not enabled. This means the context manager silently does nothing, if the global lock is not available.