labthings_fastapi.properties
Define properties of Thing objects.
Properties are attributes of a Thing that may be read or written to
over HTTP, and they are described in Generated documentation. They are implemented with
a function property (usually referenced as lt.property), which is
intentionally similar to Python’s built in property.
Properties can be defined in two ways as shown below:
import labthings_fastapi as lt
class Counter(lt.Thing):
"A counter that knows what's remaining."
count: int = lt.property(default=0, readonly=True)
"The number of times we've incremented the counter."
target: int = lt.property(default=10)
"The number of times to increment before we stop."
@lt.property
def remaining(self) -> int:
"The number of steps remaining."
return self.target - self.count
@remaining.setter
def _set_remaining(self, value: int) -> None:
self.target = self.count + value
The first two properties are simple variables: they may be read and assigned
to, and will behave just like a regular variable. Their syntax is similar to
dataclasses or pydantic in that property is used as a “field specifier”
to set options like the default value, and the type annotation is on the
class attribute. Documentation is in strings immediately following the
properties, which is understood by most automatic documentation tools.
remaining is defined using a “getter” function, meaning this code will
be run each time counter.remaining is accessed. Its type will be the
return type of the function, and its docstring will come from the function
too. Setters with only a getter are read-only.
Adding a “setter” to properties is optional, and makes them read-write.
Attributes
The set of supported constraint arguments for properties. |
|
The value returned by a property. |
|
The |
|
An instance of (a subclass of) BaseProperty. |
Exceptions
The default value has been specified more than once. |
|
The default value has not been specified. |
Classes
Constraints that may be applied to a |
|
A descriptor that marks Properties on Things. |
|
A Property descriptor that acts like a regular variable. |
|
A property that uses a getter and a setter. |
|
Access to the metadata of a Property. |
|
Access to metadata on all the properties of a |
|
A base class for settings. |
|
A |
|
A |
|
Access to the metadata of a setting. |
|
Access to metadata on all the properties of a |
Functions
|
Process default arguments to get a default factory function. |
|
Define a Property on a |
|
Define a Setting on a |
Module Contents
- labthings_fastapi.properties.CONSTRAINT_ARGS
The set of supported constraint arguments for properties.
- class labthings_fastapi.properties.FieldConstraints
Bases:
TypedDictConstraints that may be applied to a
property.Initialize self. See help(type(self)) for accurate signature.
- exception labthings_fastapi.properties.OverspecifiedDefaultError
Bases:
ValueErrorThe default value has been specified more than once.
This error is raised when a
DataPropertyis instantiated with both adefaultvalue and adefault_factoryprovided.Initialize self. See help(type(self)) for accurate signature.
- exception labthings_fastapi.properties.MissingDefaultError
Bases:
ValueErrorThe default value has not been specified.
This error is raised when a
DataPropertyis instantiated without adefaultvalue or adefault_factoryfunction.Initialize self. See help(type(self)) for accurate signature.
- labthings_fastapi.properties.Value
The value returned by a property.
- labthings_fastapi.properties.BasePropertyT
An instance of (a subclass of) BaseProperty.
- labthings_fastapi.properties.default_factory_from_arguments(default: Value | types.EllipsisType = ..., default_factory: Callable[[], Value] | None = None) Callable[[], Value]
Process default arguments to get a default factory function.
This function takes the
defaultanddefault_factoryarguments and will either return thedefault_factoryif it is provided, or will wrap the default value provided in a factory function.Note that this wrapping does not copy the default value each time it is called, so mutable default values are only safe if supplied as a factory function.
This is used to avoid repeating the logic of checking whether a default value or a factory function has been provided, and it returns a factory rather than a default value so that it may be called multiple times to get copies of the default value.
This function also ensures the default is specified exactly once, and raises exceptions if it is not.
This logic originally lived only in the initialiser of
DataPropertybut it was needed in thepropertyandsettingfunctions in order to correctly type them (so that specifying both or neither of thedefaultanddefault_factoryarguments would raise an error with mypy).- Parameters:
default – the default value, or an ellipsis if not specified.
default_factory – a function that returns the default value.
- Returns:
a function that returns the default value.
- Raises:
OverspecifiedDefaultError – if both
defaultanddefault_factoryare specified.MissingDefaultError – if neither
defaultnordefault_factoryare specified.
- labthings_fastapi.properties.property(getter: Callable[[Owner], Value]) FunctionalProperty[Owner, Value]
- labthings_fastapi.properties.property(*, default: Value, readonly: bool = False, use_global_lock: Literal[False] | None = None, **constraints: Any) Value
- labthings_fastapi.properties.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
propertyas a decorator.default – is the default value. Either this,
getterordefault_factorymust 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
defaultif you need to use a mutable datatype. For example, it would be better to specifydefault_factory=listthandefault=[]because the second form would be shared between allThings with this property.readonly – whether the property should be read-only via the
ThingClientinterface (i.e. over HTTP or via aDirectThingClient). This is automatically true ifpropertyis used as a decorator and no setter is specified.use_global_lock – may be set to
Falseto 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.Fieldand allow constraints to be added to the property. For example,ge=0constrains a numeric property to be non-negative. Seepydantic.Fieldfor the full range of constraint arguments.
- Returns:
a property descriptor, either a
FunctionalPropertyif used as a decorator, or aDataPropertyif used as a field.- Raises:
MissingDefaultError – if no valid default value is supplied, and a getter is not in use.
OverspecifiedDefaultError – if the default is specified more than once (e.g.
default,default_factory, orgetter).
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
defaultperforms 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 meanspropertydoes not know its type hint until after it’s been called.When used as a field specifier,
propertyreturns a genericDataPropertydescriptor instance, which will determine its type when it is attached to theThing. The type hint on the return value ofpropertyin that situation is a “white lie”: we annotate the return as having the same type as thedefaultvalue (or thedefault_factoryreturn value). This means that type checkers such asmypywill check that the default is valid for the type of the field, and won’t raise an error about assigning, for example, an instance ofDataProperty[int]to a field annotated asint.Finally, the type of the
defaultargument includesEllipsisTypeso that we can use...as its default value. This allows us to distinguish betweendefaultnot being set (...) and a desired default value ofNone. Similarly,...is the default value forgetterso we can raise a more helpful error if a non-callable value is passed as the first argument.
- class labthings_fastapi.properties.BaseProperty(constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
Bases:
labthings_fastapi.base_descriptor.FieldTypedBaseDescriptor[Owner,Value],Generic[Owner,Value]A descriptor that marks Properties on Things.
This class is used to determine whether an attribute of a
Thingshould be treated as a Property (see Properties - essentially, it means the value should be available over HTTP).BasePropertyshould not be used directly, instead it is recommended to usepropertyto declare properties on yourThingsubclass.Initialise a BaseProperty.
- Parameters:
constraints – is passed as keyword arguments to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details. The module-level constantCONSTRAINT_ARGSlists the supported constraint arguments.use_global_lock – may be set to
Falseto disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.
- Raises:
UnsupportedConstraintError – if unsupported constraint arguments are supplied. See
CONSTRAINT_ARGSfor the supported arguments.
- _model: type[pydantic.BaseModel] | None = None
- _constraints: FieldConstraints
- use_global_lock = None
- observable: bool = False
Whether or not the property may be observed.
If
observableisTruethen a websocket connection can register to be notified when the property changes. By default this isTruefor data properties andFalsefor functional properties.
- static _validate_constraints(constraints: collections.abc.Mapping[str, Any]) FieldConstraints
Validate an untyped dictionary of constraints.
- Parameters:
constraints – A mapping that will be validated against the
FieldConstraintstyped dictionary.- Returns:
A
FieldConstraintsinstance.- Raises:
UnsupportedConstraintError – if the input is not valid.
- constraints() FieldConstraints
Validation constraints applied to this property.
This mapping contains keyword arguments that will be passed to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details. The module-level constantCONSTRAINT_ARGSlists the supported constraint arguments.Note that these constraints will be enforced when values are received over HTTP, but they are not automatically enforced when setting the property directly on the
Thinginstance from Python code.
- model() type[pydantic.BaseModel]
A Pydantic model for the property’s type.
pydanticmodels are used to serialise and deserialise values from and to JSON. If the property is defined with a type hint that is not apydantic.BaseModelsubclass, this property will ensure it is wrapped in apydantic.RootModelso it can be used with FastAPI.If
BaseProperty.value_typeis already apydantic.BaseModelsubclass, this returns it unchanged.- Returns:
a Pydantic model for the property’s type.
- Raises:
UnserialisableTypeError – if the property can’t be serialised by
pydanticto JSON.
- get_default(obj: Owner | None) Value
Return the default value of this property.
- Parameters:
obj – the
Thinginstance on which we are looking for the default. orNoneif referring to the class. For now, this is ignored.- Returns:
the default value of this property.
- Raises:
FeatureNotAvailableError – as this must be overridden.
- reset(obj: Owner) None
Reset the property’s value to a default state.
If there is a defined default value for the property, this method should reset the property to that default.
Not every property is expected to implement
resetso it is important to handleFeatureNotAvailableErrorexceptions, which will be raised if this method is not overridden.- Parameters:
obj – the
Thinginstance we want to reset.- Raises:
FeatureNotAvailableError – as only some subclasses implement resetting.
- is_resettable(obj: Owner | None) bool
Determine if it’s possible to reset this property.
By default, this returns
Trueifresethas been overridden. If you overrideresetbut want more control over this behaviour, you probably need to overrideis_resettable.
- add_to_fastapi(app: fastapi.FastAPI, thing: Owner) 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
Thingwe are adding the endpoints for.
- Raises:
NotConnectedToServerError – if the
Thingdoes not have apathset.
- property_affordance(thing: Owner, path: str | None = None) labthings_fastapi.thing_description._model.PropertyAffordance
Represent the property in a Thing Description.
- Parameters:
- Returns:
A description of the property in Thing Description format.
- Raises:
NotConnectedToServerError – if the
Thingdoes not have apathset.
- abstract __set__(obj: Owner, value: Any) None
Set the property (stub method).
This is a stub
__set__method to mark this as a data descriptor.- Parameters:
obj – The Thing on which we are setting the value.
value – The new value for the Thing.
- Raises:
NotImplementedError – as this must be overridden by concrete classes.
- descriptor_info(owner: Owner | None = None) PropertyInfo[Self, Owner, Value]
Return an object that allows access to this descriptor’s metadata.
- Parameters:
owner – An instance to bind the descriptor info to. If
None, the returned object will be unbound and will only refer to the class.- Returns:
A
PropertyInfoinstance describing this property.
- class labthings_fastapi.properties.DataProperty(default: Value, *, readonly: bool = False, constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
- class labthings_fastapi.properties.DataProperty(*, default_factory: Callable[[], Value], readonly: bool = False, constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
Bases:
BaseProperty[Owner,Value],Generic[Owner,Value]A Property descriptor that acts like a regular variable.
DataPropertydescriptors remember their value, and can be read and written to like a regular Python variable.Create a property that acts like a regular variable.
DataPropertydescriptors function just like variables, in that they can be read and written to as attributes of theThingand their value will be the same every time it is read (i.e. it changes only when it is set). This differs fromFunctionalPropertywhich uses a “getter” function just likebuiltins.propertyand may return a different value each time.DataPropertyinstances may always be set, when they are accessed as an attribute of theThinginstance. Thereadonlyparameter applies only to client code, whether it is remote or aDirectThingClientwrapper.The type of the property’s value will be inferred either from the type subscript or from an annotation on the class attribute. This is done in
__get_name__because neither is available during__init__.- Parameters:
default – the default value. This or
default_factorymust be provided. Note that, asNoneis a valid default value, this uses...instead as a way of checking whetherdefaulthas been set.default_factory – a function that returns the default value. This is appropriate for datatypes such as lists, where using a mutable default value can lead to odd behaviour.
readonly – if
True, the property may not be written to via HTTP, or viaDirectThingClientobjects, i.e. it may only be set as an attribute of theThingand not from a client.constraints – is passed as keyword arguments to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details.use_global_lock – may be set to
Falseto disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.
- _default_factory
- readonly = False
- instance_get(obj: Owner) Value
Return the property’s value.
This will supply a default if the property has not yet been set.
- Parameters:
obj – The
Thingon which the property is being accessed.- Returns:
the value of the property.
- __set__(obj: Owner, value: Value) None
Set the property’s value.
This sets the property’s value, and notifies any observers.
If property validation is enabled by
lt.Thing._class_settingsthis will validate the value against the property’s model, and an error will be raised if the value is not valid.- Parameters:
obj – the
Thingto which we are attached.value – the new value for the property.
- observable: bool = True
Whether or not the property may be observed.
If
observableisTruethen a websocket connection can register to be notified when the property changes. By default this isTruefor data properties.
- get_default(obj: Owner | None) Value
Return the default value of this property.
Note that this implementation is independent of the
Thinginstance, as there’s currently no way to specify a per-instance default.- Parameters:
obj – the
Thinginstance we want to reset.- Returns:
the default value of this property.
- reset(obj: Owner) None
Reset the property to its default value.
This resets to the value returned by
defaultforDataProperty.- Parameters:
obj – the
Thinginstance we want to reset.
- class labthings_fastapi.properties.FunctionalProperty(fget: Callable[[Owner], Value], constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
Bases:
BaseProperty[Owner,Value],Generic[Owner,Value]A property that uses a getter and a setter.
For properties that should work like variables, use
DataProperty. For properties that need to run code every time they are read, use this class.Functional properties should work very much like Python’s
builtins.propertyexcept that they are also available over HTTP.Set up a FunctionalProperty.
Create a descriptor for a property that uses a getter function.
This class also inherits from
builtins.propertyto help type checking tools understand that it functions like a property.- Parameters:
fget – the getter function, called when the property is read.
constraints – is passed as keyword arguments to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details.use_global_lock – may be set to
Falseto disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.
- _fget
- _type
- fget() Callable[[Owner], Value]
The getter function.
- setter(fset: Callable[[Owner, Value], None]) Self
Set the setter function of the property.
This function returns the descriptor, so it may be used as a decorator.
Once a setter has been added to a property, it will automatically become writeable from client code (over HTTP and via
DirectThingClient). To override this behaviour you may setreadonlyback toTrue.class MyThing(lt.Thing): def __init__(self, thing_server_interface): super().__init__(thing_server_interface=thing_server_interface) self._myprop: int = 0 @lt.property def myprop(self) -> int: "An example property that is an integer" return self._myprop @myprop.setter def _set_myprop(self, val: int) -> None: self._myprop = val myprop.readonly = True # Prevent client code from setting it
Note
The example code above is not quite what would be done for the built-in
@propertydecorator, because our setter does not have the same name as the getter. Using a different name avoids type checkers such asmypyraising an error that the getter has been redefined with a different type. The behaviour is identical whether the setter and getter have the same name or not. The only difference is that theThingwill have an additional method called_set_mypropin the example above.- Parameters:
fset – The new setter function.
- Returns:
this descriptor (i.e.
self). This allows use as a decorator.
Typing Notes
Python’s built-in
propertyis treated as a special case bymypyand others, and our descriptor is not treated in the same way. Naming the setter and getter the same is required bybuiltins.propertybecause the property must be overwritten when the setter is added, asbuiltins.propertyis not mutable.Our descriptor is mutable, so the setter may be added without having to overwrite the object. While it would be nice to use exactly the same conventions as
builtins.property, it currently causes type errors that must be silenced manually. We suggest using a different name for the setter as an alternative to adding# type: ignore[no-redef]to the setter function.It will cause problems elsewhere in the code if descriptors are assigned to more than one attribute, and this is checked in
BaseDescriptor.__set_name__. We therefore return the setter rather than the descriptor if the names don’t match. The type hint does not reflect this, as it would cause problems when the names do match (the descriptor would become aFunctionalProperty | Callableand thus typing errors would happen whenever it’s accessed).
- instance_get(obj: Owner) Value
Get the value of the property.
- Parameters:
obj – the
Thingon which the attribute is accessed.- Returns:
the value of the property.
- __set__(obj: Owner, value: Value) None
Set the value of the property.
If property validation is enabled by
lt.Thing._class_settingsthis will validate the value against the property’s model, and an error will be raised if the value is not valid.- Parameters:
obj – the
Thingon which the attribute is accessed.value – the value of the property.
- Raises:
ReadOnlyPropertyError – if the property cannot be set.
- default() Value
The default value for this property.
This attribute is mostly provided to allow it to be set at class definition time - it should usually be retrieved through
thing_instance.properties['name'].default.Warning
The default is not guaranteed to be available! It should usually be accessed via a
PropertyInfoobject, asthing.properties['name'].default. If a default is not available, aFeatureNotAvailableErrorwill be raised.- Raises:
FeatureNotAvailableError – if no default is defined.
- Returns:
the default value.
- default_factory() Callable[[], Value] | None
The default factory function, if available.
This property will be
Noneif no default is set, or it will be a function that returns a default value.Setting the default factory will also allow this property to be reset using its
reset()method, which will call the property’s setter with the default value. If a reset function was already specified (e.g. with theresetterdecorator), it will not be overwritten.- Returns:
the default factory function, or
Noneif it is not set.
- get_default(obj: Owner | None) Value
Return a default value, if available.
- Parameters:
obj – The Thing for which we are retrieving the default value, or
Noneif we are referring only to the class.- Returns:
the default value.
- Raises:
FeatureNotAvailable – if no default has been defined.
- resetter(freset: Callable[[Owner], None]) Callable[[Owner], None]
Decorate a method that resets the property to a default state.
Functional properties may optionally define a function that resets the property to a default state. This method is intended to be used as a decorator:
import labthings_fastapi as lt class MyThing(lt.Thing): def __init__(self, **kwargs): super().__init__(**kwargs) self._myprop = 42 @lt.property def myprop(self) -> int: return self._myprop @myprop.setter def _set_myprop(self, val: int) -> None: self._myprop = val @myprop.resetter def _reset_myprop(self) -> None: self._myprop = 42
- Parameters:
freset – The method being decorated. This should take one positional argument,
self, which has the usual meaning for Python methods.- Raises:
PropertyRedefinitionError – if the decorated method has the same name as the property. Please use a different name, as shown in the example above.
- Returns:
the decorated function (unchanged). Note that we don’t return the property, so you must choose a different name for the reset function.
- reset(obj: Owner) None
Reset the property to its default value.
This resets to the value returned by
defaultforDataProperty.- Parameters:
obj – the
Thinginstance we want to reset.- Raises:
FeatureNotAvailable – if no reset method is available, which means there is no default defined, and no resetter method.
- is_resettable(obj: Owner | None) bool
Whether the property may be reset.
This will be true if a
resetterfunction has been added, or if a default is defined and the property has asetterdefined.- Parameters:
obj – the object on which we are defined.
- Returns:
whether a call to
reset()should succeed.
- class labthings_fastapi.properties.PropertyInfo(descriptor: Descriptor, obj: Owner | None, cls: type[Owner] | None = None)
Bases:
labthings_fastapi.base_descriptor.FieldTypedBaseDescriptorInfo[BasePropertyT,Owner,Value],Generic[BasePropertyT,Owner,Value]Access to the metadata of a Property.
This class provides a way to access the metadata of a Property, without needing to retrieve the Descriptor object directly. It may be bound to a
Thinginstance, or may be accessed from the class.Initialise an
OptionallyBoundInfoobject.This sets up a BaseDescriptorInfo object, describing
descriptorand optionally bound toobj.- Parameters:
descriptor – The descriptor that this object will describe.
obj – The object to which this
BaseDescriptorInfois bound. If it isNone(default), the object will be unbound and will refer to the descriptor as attached to the class. This may mean that some methods are unavailable.cls – The class to which we are bound. Only required if
objisNone.
- Raises:
ValueError – if both
objandclsareNone.
- model() type[pydantic.BaseModel]
A
pydantic.BaseModeldescribing this property’s value.
- model_instance() pydantic.BaseModel
An instance of
self.modelpopulated with the current value.- Raises:
TypeError – if the return value can’t be wrapped in a model.
- default() Value
The default value of this property.
Warning
Note that this is an optional feature, so calling code must handle
FeatureNotAvailableErrorexceptions.
- reset() None
Reset the property to a default value.
Warning
Note that this is an optional feature, so calling code must handle
FeatureNotAvailableErrorexceptions.
- validate(value: Any) Value
Use the validation logic in
self.model.This method should accept anything that
pydanticcan convert to the right type, and return a correctly-typed value. It also enforces any constraints that were set on the property.- Parameters:
value – The new value, in any form acceptable to the property’s model. Usually this means you can use either the correct type, or the value as loaded from JSON.
- Returns:
the new value, with the correct type.
- Raises:
ValidationError – if the supplied value can’t be loaded by the property’s model. This is the exception raised by
model_validateTypeError – if the property has a
modelthat’s inconsistent with its value type. This should never happen.
- class labthings_fastapi.properties.PropertyCollection(obj: Owner | None, cls: type[Owner] | None = None)
Bases:
labthings_fastapi.base_descriptor.DescriptorInfoCollection[Owner,PropertyInfo],Generic[Owner]Access to metadata on all the properties of a
Thinginstance or subclass.This object may be used as a mapping, to retrieve
PropertyInfoobjects for each Property of aThingby name. This allows easy access to metadata like their description and model.Initialise the DescriptorInfoCollection.
This initialises the object, optionally binding it to
objif it is notNone.- Parameters:
obj – The object to which this info object is bound. If it is
None(default), the object will be unbound and will refer to the descriptor as attached to the class. This may mean that some methods are unavailable.cls – The class to which this info object refers. May be omitted if
objis supplied.
- _descriptorinfo_class
The class of DescriptorInfo objects contained in this collection.
This class attribute must be set in subclasses.
- labthings_fastapi.properties.setting(getter: Callable[[Owner], Value]) FunctionalSetting[Owner, Value]
- labthings_fastapi.properties.setting(*, default: Value, readonly: bool = False, use_global_lock: Literal[False] | None = None, **constraints: Any) Value
- labthings_fastapi.properties.setting(*, default_factory: Callable[[], Value], readonly: bool = False, use_global_lock: Literal[False] | None = None, **constraints: Any) Value
Define a Setting on a
Thing.A setting is a property that is saved to disk.
This function defines a setting, which is a special Property that will be saved to disk, so it persists even when the LabThings server is restarted. It is otherwise very similar to
property.A type annotation is required, and should follow the same constraints as for
@property.Every
settingon aThingwill be read each time the settings are saved, which may be quite frequent. This means your getter must not take too long to run, or have side-effects. Settings that use getters and setters may be removed in the future pending the outcome of #159.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
dictsettingholding the dictionary{"a": 1, "b": 2}thenself.dictsetting = {"a": 2, "b": 2}would trigger saving butself.dictsetting[a] = 2would not, as the setter fordictsettingis never called.- Parameters:
getter – is a method of a class that returns the value of this property. This is usually supplied by using
propertyas a decorator.default – is the default value. Either this,
getterordefault_factorymust 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
defaultif you need to use a mutable datatype. For example, it would be better to specifydefault_factory=listthandefault=[]because the second form would be shared between allThings with this setting.readonly – whether the setting should be read-only via the
ThingClientinterface (i.e. over HTTP or via aDirectThingClient).use_global_lock – may be set to
Falseto disable the global lock for setting this setting. By default, if global locking is enabled, we hold the global lock while setting the setting.**constraints – additional keyword arguments are passed to
pydantic.Fieldand allow constraints to be added to the setting. For example,ge=0constrains a numeric setting to be non-negative. Seepydantic.Fieldfor the full range of constraint arguments.
- Returns:
a setting descriptor.
- Raises:
MissingDefaultError – if no valid default or getter is supplied.
OverspecifiedDefaultError – if the default is specified more than once (e.g.
default,default_factory, orgetter).
Typing Notes
See the typing notes on
propertyas they all apply tosettingas well.
- class labthings_fastapi.properties.BaseSetting(constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
Bases:
BaseProperty[Owner,Value],Generic[Owner,Value]A base class for settings.
This is a subclass of
BasePropertythat is used to define settings. It is not intended to be used directly, but viasettingand the two concrete implementations:DataSettingandFunctionalSetting.Initialise a BaseProperty.
- Parameters:
constraints – is passed as keyword arguments to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details. The module-level constantCONSTRAINT_ARGSlists the supported constraint arguments.use_global_lock – may be set to
Falseto disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.
- Raises:
UnsupportedConstraintError – if unsupported constraint arguments are supplied. See
CONSTRAINT_ARGSfor the supported arguments.
- descriptor_info(owner: Owner | None = None) SettingInfo[Owner, Value]
Return an object that allows access to this descriptor’s metadata.
- Parameters:
owner – An instance to bind the descriptor info to. If
None, the returned object will be unbound and will only refer to the class.- Returns:
A
SettingInfoinstance describing this setting.
- class labthings_fastapi.properties.DataSetting(default: Value, *, readonly: bool = False, constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
- class labthings_fastapi.properties.DataSetting(*, default_factory: Callable[[], Value], readonly: bool = False, constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
Bases:
DataProperty[Owner,Value],BaseSetting[Owner,Value],Generic[Owner,Value]A
DataPropertythat persists on disk.A setting can be accessed via the HTTP API and is persistent between sessions.
A
DataSettingis aDataPropertywith extra functionality for triggering aThingto 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
dictsettingholding the dictionary{"a": 1, "b": 2}thenself.dictsetting = {"a": 2, "b": 2}would trigger saving butself.dictsetting[a] = 2would not, as the setter fordictsettingis never called.The setting otherwise acts just like a normal variable.
Create a property that acts like a regular variable.
DataPropertydescriptors function just like variables, in that they can be read and written to as attributes of theThingand their value will be the same every time it is read (i.e. it changes only when it is set). This differs fromFunctionalPropertywhich uses a “getter” function just likebuiltins.propertyand may return a different value each time.DataPropertyinstances may always be set, when they are accessed as an attribute of theThinginstance. Thereadonlyparameter applies only to client code, whether it is remote or aDirectThingClientwrapper.The type of the property’s value will be inferred either from the type subscript or from an annotation on the class attribute. This is done in
__get_name__because neither is available during__init__.- Parameters:
default – the default value. This or
default_factorymust be provided. Note that, asNoneis a valid default value, this uses...instead as a way of checking whetherdefaulthas been set.default_factory – a function that returns the default value. This is appropriate for datatypes such as lists, where using a mutable default value can lead to odd behaviour.
readonly – if
True, the property may not be written to via HTTP, or viaDirectThingClientobjects, i.e. it may only be set as an attribute of theThingand not from a client.constraints – is passed as keyword arguments to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details.use_global_lock – may be set to
Falseto disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.
- class labthings_fastapi.properties.FunctionalSetting(fget: Callable[[Owner], Value], constraints: collections.abc.Mapping[str, Any] | None = None, use_global_lock: Literal[False] | None = None)
Bases:
FunctionalProperty[Owner,Value],BaseSetting[Owner,Value],Generic[Owner,Value]A
FunctionalPropertythat persists on disk.A setting can be accessed via the HTTP API and is persistent between sessions.
A
FunctionalSettingis aFunctionalPropertywith extra functionality for triggering aThingto 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
dictsettingholding the dictionary{"a": 1, "b": 2}thenself.dictsetting = {"a": 2, "b": 2}would trigger saving butself.dictsetting[a] = 2would not, as the setter fordictsettingis never called.The setting otherwise acts just like a
FunctionalProperty`, i.e. it uses a getter and a setter function.Set up a FunctionalProperty.
Create a descriptor for a property that uses a getter function.
This class also inherits from
builtins.propertyto help type checking tools understand that it functions like a property.- Parameters:
fget – the getter function, called when the property is read.
constraints – is passed as keyword arguments to
pydantic.Fieldto add validation constraints to the property. Seepydantic.Fieldfor details.use_global_lock – may be set to
Falseto disable the global lock for setting this property. By default, if global locking is enabled, we hold the global lock while setting the property.
- class labthings_fastapi.properties.SettingInfo(descriptor: Descriptor, obj: Owner | None, cls: type[Owner] | None = None)
Bases:
PropertyInfo[BaseSetting[Owner,Value],Owner,Value],Generic[Owner,Value]Access to the metadata of a setting.
Initialise an
OptionallyBoundInfoobject.This sets up a BaseDescriptorInfo object, describing
descriptorand optionally bound toobj.- Parameters:
descriptor – The descriptor that this object will describe.
obj – The object to which this
BaseDescriptorInfois bound. If it isNone(default), the object will be unbound and will refer to the descriptor as attached to the class. This may mean that some methods are unavailable.cls – The class to which we are bound. Only required if
objisNone.
- Raises:
ValueError – if both
objandclsareNone.
- class labthings_fastapi.properties.SettingCollection(obj: Owner | None, cls: type[Owner] | None = None)
Bases:
labthings_fastapi.base_descriptor.DescriptorInfoCollection[Owner,SettingInfo],Generic[Owner]Access to metadata on all the properties of a
Thinginstance or subclass.This object may be used as a mapping, to retrieve
PropertyInfoobjects for each Property of aThingby name. This allows easy access to metadata like their description and model.Initialise the DescriptorInfoCollection.
This initialises the object, optionally binding it to
objif it is notNone.- Parameters:
obj – The object to which this info object is bound. If it is
None(default), the object will be unbound and will refer to the descriptor as attached to the class. This may mean that some methods are unavailable.cls – The class to which this info object refers. May be omitted if
objis supplied.
- _descriptorinfo_class
The class of DescriptorInfo objects contained in this collection.
This class attribute must be set in subclasses.
- model() type[pydantic.BaseModel]
A
pydantic.BaseModelrepresenting all the settings.This
pydantic.BaseModelis used to load and save the settings to a file. Note that it uses themodelof each setting, so every field in this model will be either aBaseModelor aRootModelinstance, unless it is missing.Wrapping plain types in a
RootModelmakes no difference to the JSON, but it means that constraints will be applied and it makes it easier to distinguish between missing fields and fields that are set toNone.
- model_instance() pydantic.BaseModel
An instance of
self.modelpopulated with the current setting values.