2929from bec_lib .endpoints import MessageEndpoints
3030from bec_lib .file_utils import DeviceConfigWriter
3131from bec_lib .logger import bec_logger
32- from bec_lib .messages import ConfigAction
32+ from bec_lib .messages import ConfigAction , sanitize_one_way_encodable
3333from bec_lib .utils .import_utils import lazy_import_from
3434from bec_lib .utils .json_extended import ExtendedEncoder
3535
3636if TYPE_CHECKING : # pragma: no cover
3737 from bec_lib .devicemanager import DeviceManagerBase
38- from bec_lib .messages import DeviceConfigMessage , RequestResponseMessage , ServiceResponseMessage
38+ from bec_lib .messages import (
39+ DeviceConfigMessage ,
40+ RequestResponseMessage ,
41+ ServiceResponseMessage ,
42+ )
3943 from bec_lib .redis_connector import RedisConnector
4044
4145else :
@@ -74,7 +78,11 @@ def __init__(self, device_manager: DeviceManagerBase) -> None:
7478 self ._config_helper = device_manager .config_helper
7579
7680 def update_session_with_file (
77- self , file_path : str , save_recovery : bool = True , force : bool = False , validate : bool = True
81+ self ,
82+ file_path : str ,
83+ save_recovery : bool = True ,
84+ force : bool = False ,
85+ validate : bool = True ,
7886 ) -> None :
7987 """Update the current session with a yaml file from disk.
8088
@@ -96,15 +104,19 @@ def save_current_session(self, file_path: str):
96104 """
97105 self ._config_helper .save_current_session (file_path )
98106
99- def reset_config (self , wait_for_response : bool = True , timeout_s : float | None = None ) -> None :
107+ def reset_config (
108+ self , wait_for_response : bool = True , timeout_s : float | None = None
109+ ) -> None :
100110 """
101111 Send a request to reset config to default
102112 Args:
103113 wait_for_response (bool): whether to wait for the response, default True
104114 timeout_s (float, optional): how long to wait for a response. Ignored if not waiting. Defaults to best effort calculated value based on message length.
105115 Returns: None
106116 """
107- self ._config_helper .reset_config (wait_for_response = wait_for_response , timeout_s = timeout_s )
117+ self ._config_helper .reset_config (
118+ wait_for_response = wait_for_response , timeout_s = timeout_s
119+ )
108120
109121 def load_demo_config (self , force : bool = False ) -> None :
110122 """
@@ -139,7 +151,11 @@ def __init__(
139151 self ._device_manager = device_manager
140152
141153 def update_session_with_file (
142- self , file_path : str , save_recovery : bool = True , force : bool = False , validate : bool = True
154+ self ,
155+ file_path : str ,
156+ save_recovery : bool = True ,
157+ force : bool = False ,
158+ validate : bool = True ,
143159 ) -> None :
144160 """Update the current session with a yaml file from disk.
145161
@@ -167,7 +183,9 @@ def update_session_with_file(
167183
168184 if not os .path .exists (self ._base_path_recovery ):
169185 self ._writer_mixin .create_directory (self ._base_path_recovery )
170- fname = os .path .join (self ._base_path_recovery , f"recovery_config_{ time_stamp } .yaml" )
186+ fname = os .path .join (
187+ self ._base_path_recovery , f"recovery_config_{ time_stamp } .yaml"
188+ )
171189 success = self ._save_config_to_file (fname , raise_on_error = False )
172190 if success :
173191 print (f"A recovery config was written to { fname } ." )
@@ -259,7 +277,9 @@ def _get_config_conflicts(self, new_config: dict, validate: bool = True) -> dict
259277 for sub_element in all_config_keys :
260278 sub_value = value .get (sub_element , None )
261279 current_sub_value = (
262- current_value .get (sub_element , None ) if current_value else None
280+ current_value .get (sub_element , None )
281+ if current_value
282+ else None
263283 )
264284 if sub_value != current_sub_value :
265285 nested_conflicts [sub_element ] = {
@@ -414,12 +434,16 @@ def _resolve_single_conflict(
414434
415435 if choice .lower () == "a" :
416436 # Accept new value
417- self ._apply_config_value (config , device_name , element , sub_element , new_value )
437+ self ._apply_config_value (
438+ config , device_name , element , sub_element , new_value
439+ )
418440 console .print (" [green]→ Accepted new value[/green]" )
419441 elif choice .lower () == "k" :
420442 # Keep current value - do nothing to config since we're loading from file
421443 # We need to update config dict to keep current value
422- self ._apply_config_value (config , device_name , element , sub_element , current_value )
444+ self ._apply_config_value (
445+ config , device_name , element , sub_element , current_value
446+ )
423447 console .print (" [yellow]→ Kept current value[/yellow]" )
424448 elif choice .lower () == "m" :
425449 # Manual merge - prompt for custom value
@@ -434,7 +458,9 @@ def _resolve_single_conflict(
434458 except (ValueError , SyntaxError ):
435459 # If evaluation fails, treat as string
436460 custom_value = custom_input
437- self ._apply_config_value (config , device_name , element , sub_element , custom_value )
461+ self ._apply_config_value (
462+ config , device_name , element , sub_element , custom_value
463+ )
438464 console .print (f" [blue]→ Applied custom value: { custom_value } [/blue]" )
439465
440466 def _format_value_for_display (self , value ) -> str :
@@ -458,7 +484,12 @@ def _format_value_for_display(self, value) -> str:
458484 return str (value )
459485
460486 def _apply_config_value (
461- self , config : dict , device_name : str , element : str , sub_element : str | None , value
487+ self ,
488+ config : dict ,
489+ device_name : str ,
490+ element : str ,
491+ sub_element : str | None ,
492+ value ,
462493 ) -> None :
463494 """
464495 Apply a resolved value to the config dict.
@@ -501,7 +532,11 @@ def _apply_all_current_values(self, config: dict, conflicts: dict) -> None:
501532 # This is a nested conflict (like deviceConfig)
502533 for sub_element , sub_conflict in conflict_data .items ():
503534 self ._apply_config_value (
504- config , device_name , element , sub_element , sub_conflict ["current" ]
535+ config ,
536+ device_name ,
537+ element ,
538+ sub_element ,
539+ sub_conflict ["current" ],
505540 )
506541
507542 def _is_nested_conflict (self , conflict_data : dict ) -> bool :
@@ -515,7 +550,8 @@ def _is_nested_conflict(self, conflict_data: dict) -> bool:
515550 bool: True if nested conflict, False otherwise.
516551 """
517552 return isinstance (conflict_data , dict ) and all (
518- isinstance (v , dict ) and "new" in v and "current" in v for v in conflict_data .values ()
553+ isinstance (v , dict ) and "new" in v and "current" in v
554+ for v in conflict_data .values ()
519555 )
520556
521557 def _save_config_to_file (self , file_path : str , raise_on_error : bool = True ) -> bool :
@@ -617,14 +653,22 @@ def send_config_request(
617653 request_id = str (uuid .uuid4 ())
618654 self ._connector .send (
619655 MessageEndpoints .device_config_request (),
620- DeviceConfigMessage (action = action , config = config , metadata = {"RID" : request_id }),
656+ DeviceConfigMessage (
657+ action = action ,
658+ config = sanitize_one_way_encodable (config ),
659+ metadata = {"RID" : request_id },
660+ ),
621661 )
622662
623663 if wait_for_response :
624- timeout = timeout_s if timeout_s is not None else self .suggested_timeout_s (config )
664+ timeout = (
665+ timeout_s if timeout_s is not None else self .suggested_timeout_s (config )
666+ )
625667 logger .info (f"Waiting for reply with timeout { timeout } s" )
626668 reply = self .wait_for_config_reply (
627- request_id , timeout = timeout , send_cancel_on_interrupt = (action != "cancel" )
669+ request_id ,
670+ timeout = timeout ,
671+ send_cancel_on_interrupt = (action != "cancel" ),
628672 )
629673 if action == "cancel" :
630674 raise DeviceConfigError (
@@ -633,7 +677,9 @@ def send_config_request(
633677 self .handle_update_reply (reply , request_id , timeout )
634678 return request_id
635679
636- def reset_config (self , wait_for_response : bool = True , timeout_s : float | None = None ) -> None :
680+ def reset_config (
681+ self , wait_for_response : bool = True , timeout_s : float | None = None
682+ ) -> None :
637683 """
638684 Send a request to reset config to default
639685 Args:
@@ -657,7 +703,9 @@ def reset_config(self, wait_for_response: bool = True, timeout_s: float | None =
657703 def suggested_timeout_s (config : dict ):
658704 return min (300 , len (config ) * 30 ) + 2
659705
660- def handle_update_reply (self , reply : RequestResponseMessage , RID : str , timeout : float ):
706+ def handle_update_reply (
707+ self , reply : RequestResponseMessage , RID : str , timeout : float
708+ ):
661709 if not reply .content ["accepted" ] and not reply .metadata .get ("updated_config" ):
662710 raise DeviceConfigError (
663711 f"Failed to update the config: { reply .content ['message' ]} . No devices were updated."
@@ -695,7 +743,9 @@ def wait_for_service_response(self, RID: str, timeout: float = 60) -> None:
695743 start_time = time .monotonic ()
696744 while True :
697745 elapsed_time = time .monotonic () - start_time
698- service_messages = self ._connector .lrange (MessageEndpoints .service_response (RID ), 0 , - 1 )
746+ service_messages = self ._connector .lrange (
747+ MessageEndpoints .service_response (RID ), 0 , - 1
748+ )
699749 if not service_messages :
700750 time .sleep (0.005 )
701751 else :
@@ -738,11 +788,15 @@ def wait_for_config_reply(
738788 start = time .monotonic ()
739789 while True :
740790 elapsed_time = time .monotonic () - start
741- msg = self ._connector .get (MessageEndpoints .device_config_request_response (RID ))
791+ msg = self ._connector .get (
792+ MessageEndpoints .device_config_request_response (RID )
793+ )
742794 if msg is None :
743795 time .sleep (0.01 )
744796 if elapsed_time > timeout :
745- raise DeviceConfigError ("Timeout reached whilst waiting for config reply." )
797+ raise DeviceConfigError (
798+ "Timeout reached whilst waiting for config reply."
799+ )
746800 continue
747801 return msg
748802 except KeyboardInterrupt :
@@ -759,6 +813,8 @@ def load_demo_config(self, force: bool = False) -> None:
759813 force (bool, optional): Force update even if there are conflicts. Defaults to False.
760814 Returns: None
761815 """
762- dir_path = os .path .abspath (os .path .join (os .path .dirname (bec_lib .__file__ ), "./configs/" ))
816+ dir_path = os .path .abspath (
817+ os .path .join (os .path .dirname (bec_lib .__file__ ), "./configs/" )
818+ )
763819 fpath = os .path .join (dir_path , "demo_config.yaml" )
764820 self .update_session_with_file (fpath , force = force )
0 commit comments