labthings_fastapi.example_things

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

MyThing

An example Thing with a few affordances.

ThingWithBrokenAffordances

A Thing that raises exceptions in actions/properties.

ThingThatCantInstantiate

A Thing that raises an exception in __init__.

ThingThatCantStart

A Thing that raises an exception in __enter__.

Functions

lt_property(…)

Define a Property on a Thing.

Package Contents

labthings_fastapi.example_things.lt_property(getter: Callable[[Owner], Value]) FunctionalProperty[Owner, Value]
labthings_fastapi.example_things.lt_property(*, default: Value, readonly: bool = False, use_global_lock: Literal[False] | None = None, **constraints: Any) Value
labthings_fastapi.example_things.lt_property(*, default_factory: Callable[[], Value], readonly: bool = False, use_global_lock: Literal[False] | None = None, **constraints: Any) Value

Define a Property on a Thing.

This function may be used to define Properties in two ways, as either a decorator or a field specifier. See the examples in the Properties.

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.

Parameters:
  • getter – is a method of a class that returns the value of this property. This is usually supplied by using property as a decorator.

  • default – is the default value. Either this, getter or default_factory must be specified. Specifying both or neither will raise an exception.

  • 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 Things with this property.

  • 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.

  • use_global_lock – may be set to False to disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.

  • **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.

Returns:

a property descriptor, either a FunctionalProperty if used as a decorator, or a DataProperty if used as a field.

Raises:

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.

class labthings_fastapi.example_things.MyThing(thing_server_interface: labthings_fastapi.thing_server_interface.ThingServerInterface)

Bases: 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.

Parameters:

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.

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[list[str] | None, 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.

Parameters:
  • repeats – How many times to do it.

  • undocumented – There’s no description on this field’s type hint.

  • title – A human-readable title.

  • attempts – A list of names of attempts.

Returns:

A dictionary with strings as keys and values.

make_a_dict(extra_key: str | None = None, extra_value: str | None = None) dict[str, str | None]

Do something that returns a dict.

Parameters:
  • extra_key – An additional key.

  • extra_value – An additional value.

Returns:

a dictionary.

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).

slowly_increase_counter(increments: int = 60, delay: float = 1) None

Increment the counter slowly over a minute.

Parameters:
  • increments – how many times to increment.

  • delay – the wait time between increments.

counter: int = None

A pointless counter

foo: str = None

A pointless string for demo purposes.

action_without_arguments() None

Do something that takes no arguments.

action_with_only_kwargs(**kwargs: dict) None

Do something that takes **kwargs.

Parameters:

**kwargs – Keyword arguments.

class labthings_fastapi.example_things.ThingWithBrokenAffordances(thing_server_interface: labthings_fastapi.thing_server_interface.ThingServerInterface)

Bases: 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.

Parameters:

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.

broken_action() None

Do something that raises an exception.

Raises:

RuntimeError – every time.

broken_property() None

Raise an exception when the property is accessed.

Raises:

RuntimeError – every time.

class labthings_fastapi.example_things.ThingThatCantInstantiate(**kwargs: Any)

Bases: labthings_fastapi.thing.Thing

A Thing that raises an exception in __init__.

Fail to initialise.

Parameters:

**kwargs – keyword arguments passed to Thing.__init__

Raises:

RuntimeError – every time.

class labthings_fastapi.example_things.ThingThatCantStart(thing_server_interface: labthings_fastapi.thing_server_interface.ThingServerInterface)

Bases: 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.

Parameters:

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.

__enter__() None

Fail to start the thing.

Raises:

RuntimeError – every time.

__exit__(exc_t: Any, exc_v: Any, exc_tb: Any) None

Don’t leave the thing as we never entered.

Parameters:
  • exc_t – Exception type.

  • exc_v – Exception value.

  • exc_tb – Traceback.