11from __future__ import annotations
22
33from collections .abc import Mapping
4- from dataclasses import dataclass , field
4+ from dataclasses import dataclass
55from pathlib import Path
66from typing import Any , Literal , cast
77
1212DEFAULT_PARAMETERS_FILE = Path ("config/parameters.yaml" )
1313
1414ScalarValueType = Literal ["float" , "int" , "bool" , "str" ]
15- ValidatorKind = Literal ["numbers" , "ints" , "bool" , "enum" , "none" ]
1615ActionSafetyMode = Literal ["alwaysAllowed" , "guarded" , "blocked" ]
1716
1817_ALLOWED_VALUE_TYPES : frozenset [str ] = frozenset ({"float" , "int" , "bool" , "str" })
19- _ALLOWED_VALIDATOR_KINDS : frozenset [str ] = frozenset ({"numbers" , "ints" , "bool" , "enum" , "none" })
2018_ALLOWED_ACTION_SAFETY_MODES : frozenset [str ] = frozenset ({"alwaysAllowed" , "guarded" , "blocked" })
2119
2220
@@ -72,14 +70,6 @@ class ActionSpec:
7270 safety_mode : ActionSafetyMode = "guarded"
7371
7472
75- @dataclass (frozen = True )
76- class ValidatorSpec :
77- kind : ValidatorKind
78- min_value : float | None = None
79- max_value : float | None = None
80- choices : tuple [Any , ...] = ()
81-
82-
8373@dataclass (frozen = True )
8474class SafetySpec :
8575 min_value : float | None
@@ -95,13 +85,9 @@ class SafetySpec:
9585class ParameterSpec :
9686 name : str
9787 label : str
98- unit : str
99- value_type : ScalarValueType
10088 get_cmd : ReadCommandSpec | None
10189 set_cmd : WriteCommandSpec | None
102- vals : ValidatorSpec | None
10390 safety : SafetySpec | None
104- snapshot_value : bool = True
10591 description : str = ""
10692
10793 @property
@@ -194,54 +180,31 @@ def _parse_parameter_spec(
194180 defaults : Mapping [str , Any ],
195181) -> ParameterSpec :
196182 label = str (mapping .get ("label" , name )).strip () or name
197- unit = str (mapping .get ("unit" , "" )).strip ()
198183 description = str (mapping .get ("description" , "" )).strip ()
199184
200- raw_value_type = mapping .get ("value_type" , mapping .get ("type" , "float" ))
201- value_type_text = str (raw_value_type ).strip ().lower ()
202- if value_type_text not in _ALLOWED_VALUE_TYPES :
203- allowed = ", " .join (sorted (_ALLOWED_VALUE_TYPES ))
204- raise ValueError (
205- f"parameters.{ name } .value_type must be one of: { allowed } . Received: { raw_value_type } "
206- )
207- value_type = cast (ScalarValueType , value_type_text )
208-
209185 get_cmd = _parse_read_command (mapping .get ("get_cmd" ), context = f"parameters.{ name } .get_cmd" )
210186 set_cmd = _parse_write_command (mapping .get ("set_cmd" ), context = f"parameters.{ name } .set_cmd" )
211187
212188 if get_cmd is None and set_cmd is None :
213189 raise ValueError (f"Parameter '{ name } ' must define at least one of get_cmd or set_cmd." )
214190
215- vals = _parse_vals (
216- mapping .get ("vals" ), value_type = value_type , context = f"parameters.{ name } .vals"
217- )
218191 safety = _parse_safety (
219192 mapping .get ("safety" ),
220- vals = vals ,
221193 defaults = defaults ,
222194 context = f"parameters.{ name } .safety" ,
223195 writable = set_cmd is not None ,
224196 )
225197
226- snapshot_value = _parse_bool (
227- mapping .get ("snapshot_value" , defaults .get ("snapshot_value" , True )),
228- field_name = f"parameters.{ name } .snapshot_value" ,
229- )
230-
231198 if set_cmd is not None and safety is None :
232199 raise ValueError (f"Writable parameter '{ name } ' must include safety settings." )
233200
234201 return ParameterSpec (
235202 name = name ,
236203 label = label ,
237- unit = unit ,
238204 description = description ,
239- value_type = value_type ,
240205 get_cmd = get_cmd ,
241206 set_cmd = set_cmd ,
242- vals = vals ,
243207 safety = safety ,
244- snapshot_value = snapshot_value ,
245208 )
246209
247210
@@ -479,44 +442,9 @@ def _legacy_arg_fields_from_mapping(
479442 return tuple (fields )
480443
481444
482- def _parse_vals (value : Any , * , value_type : ScalarValueType , context : str ) -> ValidatorSpec | None :
483- if value is None :
484- if value_type == "bool" :
485- return ValidatorSpec (kind = "bool" )
486- return None
487- if value is False :
488- return None
489-
490- mapping = _as_mapping (value , context = context )
491- kind_raw = mapping .get ("kind" , _default_validator_kind (value_type ))
492- kind = str (kind_raw ).strip ().lower ()
493- if kind not in _ALLOWED_VALIDATOR_KINDS :
494- allowed = ", " .join (sorted (_ALLOWED_VALIDATOR_KINDS ))
495- raise ValueError (f"{ context } .kind must be one of: { allowed } . Received: { kind_raw } " )
496-
497- min_value = None if mapping .get ("min" ) is None else float (mapping ["min" ])
498- max_value = None if mapping .get ("max" ) is None else float (mapping ["max" ])
499- if min_value is not None and max_value is not None and max_value < min_value :
500- raise ValueError (f"{ context } : max must be >= min." )
501-
502- choices_raw = mapping .get ("choices" , ())
503- if isinstance (choices_raw , (list , tuple )):
504- choices = tuple (choices_raw )
505- else :
506- raise ValueError (f"{ context } .choices must be a list when provided." )
507-
508- return ValidatorSpec (
509- kind = cast (ValidatorKind , kind ),
510- min_value = min_value ,
511- max_value = max_value ,
512- choices = choices ,
513- )
514-
515-
516445def _parse_safety (
517446 value : Any ,
518447 * ,
519- vals : ValidatorSpec | None ,
520448 defaults : Mapping [str , Any ],
521449 context : str ,
522450 writable : bool ,
@@ -531,8 +459,8 @@ def _parse_safety(
531459 if not writable and not mapping :
532460 return None
533461
534- min_default = vals . min_value if vals is not None else None
535- max_default = vals . max_value if vals is not None else None
462+ min_default = None
463+ max_default = None
536464 max_step_default = None
537465
538466 min_value_raw = mapping .get ("min" , min_default )
@@ -589,16 +517,6 @@ def _parse_safety(
589517 )
590518
591519
592- def _default_validator_kind (value_type : ScalarValueType ) -> ValidatorKind :
593- if value_type == "float" :
594- return "numbers"
595- if value_type == "int" :
596- return "ints"
597- if value_type == "bool" :
598- return "bool"
599- return "none"
600-
601-
602520def _parse_required_string (value : Any , * , field_name : str ) -> str :
603521 text = "" if value is None else str (value ).strip ()
604522 if not text :
@@ -627,70 +545,3 @@ def _parse_bool(value: Any, *, field_name: str) -> bool:
627545 return False
628546
629547 raise ValueError (f"Invalid boolean value for { field_name } : { value } " )
630-
631-
632- @dataclass (frozen = True )
633- class ScalarParameterSpec :
634- name : str
635- command : str
636- value_type : ScalarValueType = "float"
637- unit : str = ""
638- label : str | None = None
639- payload_index : int = 0
640- args : Mapping [str , Any ] = field (default_factory = dict )
641- snapshot_value : bool = True
642-
643-
644- def load_scalar_parameter_specs (parameter_file : str | Path ) -> tuple [ScalarParameterSpec , ...]:
645- parameter_path = Path (parameter_file ).expanduser ()
646- if not parameter_path .exists ():
647- raise ValueError (f"Parameter file does not exist: { parameter_path } " )
648-
649- with parameter_path .open ("r" , encoding = "utf-8" ) as handle :
650- loaded = yaml .safe_load (handle )
651-
652- if loaded is None :
653- return ()
654-
655- root = _as_mapping (loaded , context = "root" )
656- parameters_raw = root .get ("parameters" , [])
657- if not isinstance (parameters_raw , list ):
658- raise ValueError ("Parameter file field 'parameters' must be a list." )
659-
660- specs : list [ScalarParameterSpec ] = []
661- for index , entry in enumerate (parameters_raw ):
662- context = f"parameters[{ index } ]"
663- mapping = _as_mapping (entry , context = context )
664-
665- raw_value_type = mapping .get ("value_type" , mapping .get ("type" , "float" ))
666- value_type_text = str (raw_value_type ).strip ().lower ()
667- if value_type_text not in _ALLOWED_VALUE_TYPES :
668- allowed = ", " .join (sorted (_ALLOWED_VALUE_TYPES ))
669- raise ValueError (
670- f"{ context } .value_type must be one of: { allowed } . Received: { raw_value_type } "
671- )
672-
673- payload_index = int (mapping .get ("payload_index" , 0 ))
674- if payload_index < 0 :
675- raise ValueError (f"{ context } .payload_index must be non-negative." )
676-
677- args = _as_mapping (mapping .get ("args" ), context = f"{ context } .args" )
678- specs .append (
679- ScalarParameterSpec (
680- name = _parse_required_string (mapping .get ("name" ), field_name = f"{ context } .name" ),
681- command = _parse_required_string (
682- mapping .get ("command" ), field_name = f"{ context } .command"
683- ),
684- value_type = cast (ScalarValueType , value_type_text ),
685- unit = str (mapping .get ("unit" , "" )).strip (),
686- label = (None if mapping .get ("label" ) is None else str (mapping .get ("label" )).strip ()),
687- payload_index = payload_index ,
688- args = dict (args ),
689- snapshot_value = _parse_bool (
690- mapping .get ("snapshot_value" , True ),
691- field_name = f"{ context } .snapshot_value" ,
692- ),
693- )
694- )
695-
696- return tuple (specs )
0 commit comments