From 01f74ea4f7bd51e5581492f5285509709b69da08 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 9 Jul 2026 14:06:38 +0200 Subject: [PATCH] refactor(flomni): consolidate global-var params into _GlobalVarParam descriptor Replace 11 identical global-var-backed getter/setter property pairs with a single _GlobalVarParam descriptor. Behavior is unchanged: same keys, defaults, and None-handling; stitch_x/stitch_y retain int enforcement via the descriptor's type_ guard (typeguard.check_type). Net -57 lines with no functional change. --- .../plugins/flomni/flomni.py | 169 ++++++------------ 1 file changed, 56 insertions(+), 113 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 84e610d..90abf7a 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -14,7 +14,7 @@ from bec_lib.alarm_handler import AlarmBase from bec_lib.endpoints import MessageEndpoints from bec_lib.pdf_writer import PDFWriter from bec_lib.scan_repeat import scan_repeat -from typeguard import typechecked +from typeguard import check_type, typechecked from csaxs_bec.bec_ipython_client.plugins.cSAXS import cSAXSBeamlineChecks from csaxs_bec.bec_ipython_client.plugins.flomni.flomni_optics_mixin import FlomniOpticsMixin @@ -1673,6 +1673,50 @@ class _TomoQueueProxy: return self._load()[index] +class _GlobalVarParam: + """Descriptor for a Flomni parameter backed by a BEC global variable. + + Consolidates the boilerplate getter/setter pairs that read from and write + to the BEC global-var store. The value persists across BEC client restarts + (instance attributes do not), and is readable from other client sessions + via ``client.get_global_var()``. + + The global-var key is taken from the attribute name the descriptor is + assigned to (see :meth:`__set_name__`), so it always matches the property + name -- exactly the invariant the hand-written pairs relied on. + + Parameters + ---------- + default + Value returned by the getter when the global var is unset (``None``). + type_ + Optional expected type. When given, the setter validates ``val`` with + :func:`typeguard.check_type` before storing, reproducing the + ``@typechecked`` guard that previously wrapped some setters (raising + ``TypeCheckError`` on a mismatch). + """ + + def __init__(self, default, type_=None): + self._default = default + self._type = type_ + + def __set_name__(self, owner, name): + self._key = name + + def __get__(self, obj, objtype=None): + if obj is None: + return self + val = obj.client.get_global_var(self._key) + if val is None: + return self._default + return val + + def __set__(self, obj, val): + if self._type is not None: + check_type(val, self._type) + obj.client.set_global_var(self._key, val) + + class Flomni( FlomniInitStagesMixin, FlomniSampleTransferMixin, @@ -1910,38 +1954,11 @@ class Flomni( raise TypeError(f"progress must be a dict, got {type(val).__name__!r}") self._progress_proxy._save(val) - @property - def tomo_shellstep(self): - val = self.client.get_global_var("tomo_shellstep") - if val is None: - return 1 - return val + tomo_shellstep = _GlobalVarParam(1) - @tomo_shellstep.setter - def tomo_shellstep(self, val: float): - self.client.set_global_var("tomo_shellstep", val) + tomo_countingtime = _GlobalVarParam(0.1) - @property - def tomo_countingtime(self): - val = self.client.get_global_var("tomo_countingtime") - if val is None: - return 0.1 - return val - - @tomo_countingtime.setter - def tomo_countingtime(self, val: float): - self.client.set_global_var("tomo_countingtime", val) - - @property - def manual_shift_y(self): - val = self.client.get_global_var("manual_shift_y") - if val is None: - return 0.0 - return val - - @manual_shift_y.setter - def manual_shift_y(self, val: float): - self.client.set_global_var("manual_shift_y", val) + manual_shift_y = _GlobalVarParam(0.0) @property def single_point_random_shift_max(self): @@ -2009,40 +2026,11 @@ class Flomni( else: raise ValueError("Unknown tomo_type.") - @property - def corridor_size(self): - val = self.client.get_global_var("corridor_size") - if val is None: - val = -1 - return val + corridor_size = _GlobalVarParam(-1) - @corridor_size.setter - def corridor_size(self, val: float): - self.client.set_global_var("corridor_size", val) + stitch_x = _GlobalVarParam(0, type_=int) - @property - def stitch_x(self): - val = self.client.get_global_var("stitch_x") - if val is None: - return 0 - return val - - @stitch_x.setter - @typechecked - def stitch_x(self, val: int): - self.client.set_global_var("stitch_x", val) - - @property - def stitch_y(self): - val = self.client.get_global_var("stitch_y") - if val is None: - return 0 - return val - - @stitch_y.setter - @typechecked - def stitch_y(self, val: int): - self.client.set_global_var("stitch_y", val) + stitch_y = _GlobalVarParam(0, type_=int) @property def ptycho_reconstruct_foldername(self): @@ -2056,38 +2044,11 @@ class Flomni( self.client.set_global_var("ptycho_reconstruct_foldername", val) self.reconstructor.folder_name = val # keep reconstructor in sync - @property - def tomo_angle_stepsize(self): - val = self.client.get_global_var("tomo_angle_stepsize") - if val is None: - return 10.0 - return val + tomo_angle_stepsize = _GlobalVarParam(10.0) - @tomo_angle_stepsize.setter - def tomo_angle_stepsize(self, val: float): - self.client.set_global_var("tomo_angle_stepsize", val) + golden_max_number_of_projections = _GlobalVarParam(1000.0) - @property - def golden_max_number_of_projections(self): - val = self.client.get_global_var("golden_max_number_of_projections") - if val is None: - return 1000.0 - return val - - @golden_max_number_of_projections.setter - def golden_max_number_of_projections(self, val: float): - self.client.set_global_var("golden_max_number_of_projections", val) - - @property - def tomo_stitch_overlap(self): - val = self.client.get_global_var("tomo_stitch_overlap") - if val is None: - return 0.2 - return val - - @tomo_stitch_overlap.setter - def tomo_stitch_overlap(self, val: float): - self.client.set_global_var("tomo_stitch_overlap", val) + tomo_stitch_overlap = _GlobalVarParam(0.2) @property def tomo_angle_range(self): @@ -2105,16 +2066,7 @@ class Flomni( raise ValueError("tomo_angle_range must be 180 or 360 degrees.") self.client.set_global_var("tomo_angle_range", val) - @property - def golden_projections_at_0_deg_for_damage_estimation(self): - val = self.client.get_global_var("golden_projections_at_0_deg_for_damage_estimation") - if val is None: - return 0 - return val - - @golden_projections_at_0_deg_for_damage_estimation.setter - def golden_projections_at_0_deg_for_damage_estimation(self, val: float): - self.client.set_global_var("golden_projections_at_0_deg_for_damage_estimation", val) + golden_projections_at_0_deg_for_damage_estimation = _GlobalVarParam(0) @property def zero_deg_reference_at_each_subtomo(self): @@ -2137,16 +2089,7 @@ class Flomni( def zero_deg_reference_at_each_subtomo(self, val: bool): self.client.set_global_var("zero_deg_reference_at_each_subtomo", val) - @property - def golden_ratio_bunch_size(self): - val = self.client.get_global_var("golden_ratio_bunch_size") - if val is None: - return 20 - return val - - @golden_ratio_bunch_size.setter - def golden_ratio_bunch_size(self, val: float): - self.client.set_global_var("golden_ratio_bunch_size", val) + golden_ratio_bunch_size = _GlobalVarParam(20) @property def frames_per_trigger(self):