|
1 | | -.. _tutorial_properties: |
2 | 1 | .. _properties: |
3 | 2 |
|
4 | 3 | Properties |
@@ -117,6 +116,53 @@ In the example above, ``twice_my_property`` may be set by code within ``MyThing` |
117 | 116 |
|
118 | 117 | Functional properties may not be observed, as they are not backed by a simple value. If you need to notify clients when the value changes, you can use a data property that is updated by the functional property. In the example above, ``my_property`` may be observed, while ``twice_my_property`` cannot be observed. It would be possible to observe changes in ``my_property`` and then query ``twice_my_property`` for its new value. |
119 | 118 |
|
| 119 | +.. _property_constraints: |
| 120 | + |
| 121 | +Property constraints |
| 122 | +-------------------- |
| 123 | + |
| 124 | +It's often helpful to make it clear that there are limits on the values a property can take. For example, a temperature property might only be valid between -40 and 125 degrees Celsius. LabThings allows you to specify constraints on properties using the same arguments as `pydantic.Field` definitions. These constraints will be enforced when the property is written to via HTTP, and they will also appear in the :ref:`gen_td` and :ref:`gen_docs`. The module-level constant `.property.CONSTRAINT_ARGS` lists all supported constraint arguments. |
| 125 | + |
| 126 | +We can modify the previous example to show how to add constraints to both data and functional properties: |
| 127 | + |
| 128 | +.. code-block:: python |
| 129 | +
|
| 130 | + import labthings_fastapi as lt |
| 131 | +
|
| 132 | + class AirSensor(lt.Thing): |
| 133 | + temperature: float = lt.property( |
| 134 | + default=20.0, |
| 135 | + ge=-40.0, # Greater than or equal to -40.0 |
| 136 | + le=125.0 # Less than or equal to 125.0 |
| 137 | + ) |
| 138 | + """The current temperature in degrees Celsius.""" |
| 139 | +
|
| 140 | + @lt.property |
| 141 | + def humidity(self) -> float: |
| 142 | + """The current humidity percentage.""" |
| 143 | + return self._humidity |
| 144 | +
|
| 145 | + @humidity.setter |
| 146 | + def humidity(self, value: float): |
| 147 | + """Set the current humidity percentage.""" |
| 148 | + self._humidity = value |
| 149 | +
|
| 150 | + # Add constraints to the functional property |
| 151 | + humidity.constraints = { |
| 152 | + "ge": 0.0, # Greater than or equal to 0.0 |
| 153 | + "le": 100.0 # Less than or equal to 100.0 |
| 154 | + } |
| 155 | +
|
| 156 | + sensor_name: str = lt.property(default="my_sensor", pattern="^[a-zA-Z0-9_]+$") |
| 157 | +
|
| 158 | +In the example above, the ``temperature`` property is a data property with constraints that limit its value to between -40.0 and 125.0 degrees Celsius. The ``humidity`` property is a functional property with constraints that limit its value to between 0.0 and 100.0 percent. The ``sensor_name`` property is a data property with a regex pattern constraint that only allows alphanumeric characters and underscores. |
| 159 | + |
| 160 | +Note that the constraints for functional properties are set by assigning a dictionary to the property's ``constraints`` attribute. This dictionary should contain the same keys and values as the arguments to `pydantic.Field` definitions. The `.property` decorator does not currently accept arguments, so constraints may only be set this way for functional properties and settings. |
| 161 | + |
| 162 | +.. note:: |
| 163 | + |
| 164 | + Property values are not validated when they are set directly, only via HTTP. This behaviour may change in the future. |
| 165 | + |
120 | 166 | HTTP interface |
121 | 167 | -------------- |
122 | 168 |
|
|
0 commit comments