labthings_fastapi.example_things ================================ .. py:module:: labthings_fastapi.example_things .. autoapi-nested-parse:: Example Thing subclasses, used for testing and demonstration purposes. Most of these are broken in some way and used for testing. These should be moved into the unit tests. Classes ------- .. autoapisummary:: labthings_fastapi.example_things.MyThing labthings_fastapi.example_things.ThingWithBrokenAffordances labthings_fastapi.example_things.ThingThatCantInstantiate labthings_fastapi.example_things.ThingThatCantStart Functions --------- .. autoapisummary:: labthings_fastapi.example_things.lt_property Package Contents ---------------- .. py:function:: lt_property(getter: Callable[[Any], Value]) -> FunctionalProperty[Value] lt_property(*, default: Value, readonly: bool = False, **constraints: Any) -> Value lt_property(*, default_factory: Callable[[], Value], readonly: bool = False, **constraints: Any) -> Value Define a Property on a `.Thing`\ . This function may be used to define :ref:`properties` in two ways, as either a decorator or a field specifier. See the examples in the :mod:`.property` documentation. 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 the 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`. :param getter: is a method of a class that returns the value of this property. This is usually supplied by using ``property`` as a decorator. :param default: is the default value. Either this, ``getter`` or ``default_factory`` must be specified. Specifying both or neither will raise an exception. :param default_factory: should return your default value. This may be used as an alternative to ``default`` if you need to use a mutable datatype. For example, it would be better to specify ``default_factory=list`` than ``default=[]`` because the second form would be shared between all `.Thing`\ s with this property. :param readonly: whether the property should be read-only via the `.ThingClient` interface (i.e. over HTTP or via a `.DirectThingClient`). This is automatically true if ``property`` is used as a decorator and no setter is specified. :param \**constraints: additional keyword arguments are passed to `pydantic.Field` and allow constraints to be added to the property. For example, ``ge=0`` constrains a numeric property to be non-negative. See `pydantic.Field` for the full range of constraint arguments. :return: a property descriptor, either a `.FunctionalProperty` if used as a decorator, or a `.DataProperty` if used as a field. :raises MissingDefaultError: if no valid default value is supplied, and a getter is not in use. :raises OverspecifiedDefaultError: if the default is specified more than once (e.g. ``default``, ``default_factory``, or ``getter``). **Typing Notes** This function has somewhat complicated type hints, for two reasons. Firstly, it may be used either as a decorator or as a field specifier, so ``default`` performs double duty as a default value or a getter. Secondly, when used as a field specifier the type hint for the property is attached to the attribute of the class to which the function's output is assigned. This means ``property`` does not know its type hint until after it's been called. When used as a field specifier, ``property`` returns a generic `.DataProperty` descriptor instance, which will determine its type when it is attached to the `.Thing`. The type hint on the return value of ``property`` in that situation is a "white lie": we annotate the return as having the same type as the ``default`` value (or the ``default_factory`` return value). This means that type checkers such as ``mypy`` will check that the default is valid for the type of the field, and won't raise an error about assigning, for example, an instance of ``DataProperty[int]`` to a field annotated as ``int``. Finally, the type of the ``default`` argument includes `.EllipsisType` so that we can use ``...`` as its default value. This allows us to distinguish between ``default`` not being set (``...``) and a desired default value of ``None``. Similarly, ``...`` is the default value for ``getter`` so we can raise a more helpful error if a non-callable value is passed as the first argument. .. py:class:: MyThing(thing_server_interface: labthings_fastapi.thing_server_interface.ThingServerInterface) Bases: :py:obj:`labthings_fastapi.thing.Thing` An example Thing with a few affordances. Initialise a Thing. The most important function of ``__init__`` is attaching the thing_server_interface, and setting the path. Note that `.Thing` instances are usually created by a `.ThingServer` and not instantiated directly: if you do make a `.Thing` directly, you will need to supply a `.ThingServerInterface` that is connected to a `.ThingServer` or a suitable mock object. :param thing_server_interface: The interface to the server that is hosting this Thing. It will be supplied when the `.Thing` is instantiated by the `.ThingServer` or by `.create_thing_without_server` which generates a mock interface. .. py:method:: anaction(repeats: Annotated[int, Field(description='The number of times to try the action')], undocumented: int, title: Annotated[str, Field(description='the title of the invocation')] = 'Untitled', attempts: Annotated[Optional[list[str]], Field(description='Names for each attempt - I suggest final, Final, FINAL.')] = None) -> dict[str, str] Quite a complicated action. This action has lots of parameters and is designed to confuse my schema generator. I hope it doesn't! I might even use some Markdown here: * If this renders, it supports lists * With at least two items. There is also a parameter and return block to satisfy docstring validators. This may be preferable to annotations on the arguments. :param repeats: How many times to do it. :param undocumented: There's no description on this field's type hint. :param title: A human-readable title. :param attempts: A list of names of attempts. :return: A dictionary with strings as keys and values. .. py:method:: make_a_dict(extra_key: Optional[str] = None, extra_value: Optional[str] = None) -> dict[str, Optional[str]] Do something that returns a dict. :param extra_key: An additional key. :param extra_value: An additional value. :return: a dictionary. .. py:method:: increment_counter() -> None Increment the counter property. This action doesn't do very much - all it does, in fact, is increment the counter (which may be read using the `counter` property). .. py:method:: slowly_increase_counter(increments: int = 60, delay: float = 1) -> None Increment the counter slowly over a minute. :param increments: how many times to increment. :param delay: the wait time between increments. .. py:attribute:: counter :type: int :value: None A pointless counter .. py:attribute:: foo :type: str :value: None A pointless string for demo purposes. .. py:method:: action_without_arguments() -> None Do something that takes no arguments. .. py:method:: action_with_only_kwargs(**kwargs: dict) -> None Do something that takes \**kwargs. :param \**kwargs: Keyword arguments. .. py:class:: ThingWithBrokenAffordances(thing_server_interface: labthings_fastapi.thing_server_interface.ThingServerInterface) Bases: :py:obj:`labthings_fastapi.thing.Thing` A Thing that raises exceptions in actions/properties. Initialise a Thing. The most important function of ``__init__`` is attaching the thing_server_interface, and setting the path. Note that `.Thing` instances are usually created by a `.ThingServer` and not instantiated directly: if you do make a `.Thing` directly, you will need to supply a `.ThingServerInterface` that is connected to a `.ThingServer` or a suitable mock object. :param thing_server_interface: The interface to the server that is hosting this Thing. It will be supplied when the `.Thing` is instantiated by the `.ThingServer` or by `.create_thing_without_server` which generates a mock interface. .. py:method:: broken_action() -> None Do something that raises an exception. :raise RuntimeError: every time. .. py:method:: broken_property() -> None Raise an exception when the property is accessed. :raise RuntimeError: every time. .. py:class:: ThingThatCantInstantiate(**kwargs: Any) Bases: :py:obj:`labthings_fastapi.thing.Thing` A Thing that raises an exception in __init__. Fail to initialise. :param \**kwargs: keyword arguments passed to Thing.__init__ :raise RuntimeError: every time. .. py:class:: ThingThatCantStart(thing_server_interface: labthings_fastapi.thing_server_interface.ThingServerInterface) Bases: :py:obj:`labthings_fastapi.thing.Thing` A Thing that raises an exception in __enter__. Initialise a Thing. The most important function of ``__init__`` is attaching the thing_server_interface, and setting the path. Note that `.Thing` instances are usually created by a `.ThingServer` and not instantiated directly: if you do make a `.Thing` directly, you will need to supply a `.ThingServerInterface` that is connected to a `.ThingServer` or a suitable mock object. :param thing_server_interface: The interface to the server that is hosting this Thing. It will be supplied when the `.Thing` is instantiated by the `.ThingServer` or by `.create_thing_without_server` which generates a mock interface. .. py:method:: __enter__() -> None Fail to start the thing. :raise RuntimeError: every time. .. py:method:: __exit__(exc_t: Any, exc_v: Any, exc_tb: Any) -> None Don't leave the thing as we never entered. :param exc_t: Exception type. :param exc_v: Exception value. :param exc_tb: Traceback.