From 622fa176346be37f5259605e82aaf79141bd3649 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 22 Apr 2026 00:28:10 +0100 Subject: [PATCH] Enable validation for all Things and pick correct branch This commit updates the branch to use Thing._class_settings instead of FEATURE_FLAGS for a safer way to implement new features. --- src/openflexure_microscope_server/things/__init__.py | 4 ---- src/openflexure_microscope_server/things/autofocus.py | 1 + src/openflexure_microscope_server/things/background_detect.py | 2 ++ src/openflexure_microscope_server/things/camera/__init__.py | 2 ++ .../things/camera_stage_mapping.py | 2 ++ src/openflexure_microscope_server/things/illumination.py | 2 ++ src/openflexure_microscope_server/things/scan_workflows.py | 4 ++++ src/openflexure_microscope_server/things/smart_scan.py | 2 ++ src/openflexure_microscope_server/things/stage/__init__.py | 1 + src/openflexure_microscope_server/things/stage_measure.py | 2 ++ src/openflexure_microscope_server/things/system.py | 2 ++ 11 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/openflexure_microscope_server/things/__init__.py b/src/openflexure_microscope_server/things/__init__.py index 838e649c..c497209f 100644 --- a/src/openflexure_microscope_server/things/__init__.py +++ b/src/openflexure_microscope_server/things/__init__.py @@ -9,10 +9,6 @@ from typing import Optional, Self import labthings_fastapi as lt -# To ensure consistency, we enable the feature flags we want here. This means -# they will be enabled in any code that imports a `Thing` from this package. -lt.FEATURE_FLAGS.validate_properties_on_set = True - class OFMThing(lt.Thing): """A custom LabThings Thing class for the OpenFlexure Microscope.""" diff --git a/src/openflexure_microscope_server/things/autofocus.py b/src/openflexure_microscope_server/things/autofocus.py index eebff335..695c349c 100644 --- a/src/openflexure_microscope_server/things/autofocus.py +++ b/src/openflexure_microscope_server/things/autofocus.py @@ -405,6 +405,7 @@ class AutofocusThing(lt.Thing): field of view to assess focus (autofocus and testing the success of a z-stack) """ + _class_settings = {"validate_properties_on_set": True} _cam: BaseCamera = lt.thing_slot() _stage: BaseStage = lt.thing_slot() diff --git a/src/openflexure_microscope_server/things/background_detect.py b/src/openflexure_microscope_server/things/background_detect.py index c2753a75..fcf03b9d 100644 --- a/src/openflexure_microscope_server/things/background_detect.py +++ b/src/openflexure_microscope_server/things/background_detect.py @@ -38,6 +38,8 @@ class ChannelBlankError(lt.exceptions.InvocationError): class BackgroundDetectAlgorithm(lt.Thing): """The base class for defining background detect algorithms.""" + _class_settings = {"validate_properties_on_set": True} + display_name: str = lt.property(default="Base Detector", readonly=True) def __init__(self, thing_server_interface: lt.ThingServerInterface) -> None: diff --git a/src/openflexure_microscope_server/things/camera/__init__.py b/src/openflexure_microscope_server/things/camera/__init__.py index 96840889..b9f0f2a7 100644 --- a/src/openflexure_microscope_server/things/camera/__init__.py +++ b/src/openflexure_microscope_server/things/camera/__init__.py @@ -172,6 +172,8 @@ class BaseCamera(OFMThing): ``__init__`` method of the subclass. """ + _class_settings = {"validate_properties_on_set": True} + _all_background_detectors: Mapping[str, BackgroundDetectAlgorithm] = lt.thing_slot() mjpeg_stream = lt.outputs.MJPEGStreamDescriptor() diff --git a/src/openflexure_microscope_server/things/camera_stage_mapping.py b/src/openflexure_microscope_server/things/camera_stage_mapping.py index 813c762f..341683a9 100644 --- a/src/openflexure_microscope_server/things/camera_stage_mapping.py +++ b/src/openflexure_microscope_server/things/camera_stage_mapping.py @@ -119,6 +119,8 @@ class CameraStageMapper(lt.Thing): override the ``get_xyz_position()`` and ``move_to_xyz_position()`` methods. """ + _class_settings = {"validate_properties_on_set": True} + _cam: BaseCamera = lt.thing_slot() _stage: BaseStage = lt.thing_slot() diff --git a/src/openflexure_microscope_server/things/illumination.py b/src/openflexure_microscope_server/things/illumination.py index 2e444138..ce8647b0 100644 --- a/src/openflexure_microscope_server/things/illumination.py +++ b/src/openflexure_microscope_server/things/illumination.py @@ -12,6 +12,8 @@ from .stage.sangaboard import SangaboardThing class Illumination(lt.Thing): """Base class for an illumination controller.""" + _class_settings = {"validate_properties_on_set": True} + @lt.action def set_led(self, led_on: bool = True) -> None: """Set the LED to on or off.""" diff --git a/src/openflexure_microscope_server/things/scan_workflows.py b/src/openflexure_microscope_server/things/scan_workflows.py index c92900c2..952b9dcb 100644 --- a/src/openflexure_microscope_server/things/scan_workflows.py +++ b/src/openflexure_microscope_server/things/scan_workflows.py @@ -62,6 +62,8 @@ class ScanWorkflow(Generic[SettingModelType], lt.Thing): scan planning, acquisition routine. """ + _class_settings = {"validate_properties_on_set": True} + display_name: str = lt.property(default="Base Workflow", readonly=True) ui_blurb: str = lt.property( default="If you see this message, something is wrong.", readonly=True @@ -327,6 +329,8 @@ class SmartStackCompatibleSettings(Protocol): class SmartStackMixin: """A mixin for scan workflows that use smart stacking.""" + _class_settings = {"validate_properties_on_set": True} + stack_images_to_save: int = lt.setting(default=1, ge=1, le=9) """The number of images to save in a stack. diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 5b2e54af..25b0a60b 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -119,6 +119,8 @@ class SmartScanThing(OFMThing): past scans. """ + _class_settings = {"validate_properties_on_set": True} + _cam: BaseCamera = lt.thing_slot() _stage: BaseStage = lt.thing_slot() _all_workflows: Mapping[str, ScanWorkflow] = lt.thing_slot() diff --git a/src/openflexure_microscope_server/things/stage/__init__.py b/src/openflexure_microscope_server/things/stage/__init__.py index d0bdeab6..e5b0b72b 100644 --- a/src/openflexure_microscope_server/things/stage/__init__.py +++ b/src/openflexure_microscope_server/things/stage/__init__.py @@ -106,6 +106,7 @@ class BaseStage(lt.Thing): """ _axis_names = ("x", "y", "z") + _class_settings = {"validate_properties_on_set": True} def __init__(self, thing_server_interface: lt.ThingServerInterface) -> None: """Initialise the stage. diff --git a/src/openflexure_microscope_server/things/stage_measure.py b/src/openflexure_microscope_server/things/stage_measure.py index 411e57ed..4d426a51 100644 --- a/src/openflexure_microscope_server/things/stage_measure.py +++ b/src/openflexure_microscope_server/things/stage_measure.py @@ -117,6 +117,8 @@ class ParasiticMotionError(lt.exceptions.InvocationError): class RangeofMotionThing(lt.Thing): """A class used to measure the range of motion of the stage in X and Y.""" + _class_settings = {"validate_properties_on_set": True} + _autofocus: AutofocusThing = lt.thing_slot() _cam: BaseCamera = lt.thing_slot() _csm: CameraStageMapper = lt.thing_slot() diff --git a/src/openflexure_microscope_server/things/system.py b/src/openflexure_microscope_server/things/system.py index ff6a2fed..d1d02517 100644 --- a/src/openflexure_microscope_server/things/system.py +++ b/src/openflexure_microscope_server/things/system.py @@ -46,6 +46,8 @@ class OpenFlexureSystem(lt.Thing): of the system. """ + _class_settings = {"validate_properties_on_set": True} + _microscope_id: Optional[str] = None @lt.setting