sift_py.yaml.channel
1from __future__ import annotations 2 3import re 4from typing import Any, Dict, List, Literal, Union, cast 5 6from typing_extensions import NotRequired, TypedDict 7 8from sift_py.error import _component_deprecation_warning 9from sift_py.ingestion.channel import ChannelDataTypeStrRep 10from sift_py.ingestion.config.yaml.error import YamlConfigError 11from sift_py.yaml.utils import _type_fqn 12 13_CHANNEL_REFERENCE_REGEX = re.compile(r"^\$\d+$") 14 15 16def _validate_channel_anchor(val: Any): 17 if not isinstance(val, str): 18 raise YamlConfigError._invalid_property( 19 val, 20 "<str>", 21 "&str", 22 ["channels"], 23 ) 24 25 26def _validate_channel(val: Any): 27 channel = cast(Dict[Any, Any], val) 28 29 name = channel.get("name") 30 31 if not isinstance(name, str): 32 raise YamlConfigError._invalid_property(name, "- name", "str", ["channels"]) 33 34 description = channel.get("description") 35 36 if description is not None and not isinstance(description, str): 37 raise YamlConfigError._invalid_property(description, "- description", "str", ["channels"]) 38 39 unit = channel.get("unit") 40 41 if unit is not None and not isinstance(unit, str): 42 raise YamlConfigError._invalid_property(unit, "- unit", "str", ["channels"]) 43 component = channel.get("component") 44 if component is not None: 45 _component_deprecation_warning() 46 if not isinstance(component, str): 47 raise YamlConfigError._invalid_property(component, "- component", "str", ["channels"]) 48 49 data_type = channel.get("data_type") 50 valid_data_type_values = [v.value for v in ChannelDataTypeStrRep] 51 52 if not data_type in valid_data_type_values: 53 raise YamlConfigError._invalid_property( 54 data_type, 55 "- data_type", 56 " | ".join(valid_data_type_values), 57 ["channels"], 58 ) 59 60 if data_type == ChannelDataTypeStrRep.ENUM.value: 61 enum_types = channel.get("enum_types") 62 63 if not isinstance(enum_types, list): 64 raise YamlConfigError._invalid_property( 65 enum_types, 66 "- enum_types", 67 f"List<{_type_fqn(ChannelEnumTypeYamlSpec)}>", 68 ["channels"], 69 ) 70 71 for enum_type in cast(List[Any], enum_types): 72 _validate_enum_type(enum_type) 73 74 elif data_type == ChannelDataTypeStrRep.BIT_FIELD.value: 75 bit_field_elements = channel.get("bit_field_elements") 76 77 if not isinstance(bit_field_elements, list): 78 raise YamlConfigError._invalid_property( 79 bit_field_elements, 80 "- bit_field_elements", 81 f"List<{_type_fqn(ChannelBitFieldElementYamlSpec)}>", 82 ) 83 84 for bit_field_element in cast(List[Any], bit_field_elements): 85 _validate_bit_field_element(bit_field_element) 86 87 else: 88 enum_types = channel.get("enum_types") 89 90 if enum_types is not None: 91 raise YamlConfigError( 92 f"Channel of data-type '{data_type}' should not have 'enum_types' set." 93 ) 94 95 bit_field_elements = channel.get("bit_field_elements") 96 97 if bit_field_elements is not None: 98 raise YamlConfigError( 99 f"Channel of data-type '{data_type}' should not have 'bit_field_elements' set." 100 ) 101 102 103def _validate_enum_type(val: Any): 104 enum_type = cast(Dict[Any, Any], val) 105 106 name = enum_type.get("name") 107 108 if not isinstance(name, str): 109 raise YamlConfigError._invalid_property( 110 name, 111 "- name", 112 "str", 113 ["channels", "- enum_type"], 114 ) 115 116 key = enum_type.get("key") 117 118 if not isinstance(key, int): 119 raise YamlConfigError._invalid_property( 120 key, 121 "- key", 122 "int", 123 ["channels", "- enum_type"], 124 ) 125 126 127def _validate_bit_field_element(val: Any): 128 bit_field_element = cast(Dict[Any, Any], val) 129 130 name = bit_field_element.get("name") 131 132 if not isinstance(name, str): 133 raise YamlConfigError._invalid_property( 134 name, "- name", "str", ["channels", "- bit_field_elements"] 135 ) 136 137 index = bit_field_element.get("index") 138 139 if not isinstance(index, int): 140 raise YamlConfigError._invalid_property( 141 name, "- index", "int", ["channels", "- bit_field_elements"] 142 ) 143 144 bit_count = bit_field_element.get("bit_count") 145 146 if not isinstance(bit_count, int): 147 raise YamlConfigError._invalid_property( 148 name, "- bit_count", "int", ["channels", "- bit_field_elements"] 149 ) 150 151 152def _validate_channel_reference(val: Any): 153 channel_reference = cast(Dict[Any, Any], val) 154 155 for key, value in channel_reference.items(): 156 if not isinstance(key, str): 157 raise YamlConfigError._invalid_property( 158 channel_reference, 159 "- <str>", 160 f"Dict[str, {_type_fqn(ChannelConfigYamlSpec)}]", 161 ["rules", "- channel_references"], 162 ) 163 164 if _CHANNEL_REFERENCE_REGEX.match(key) is None: 165 raise YamlConfigError( 166 f"Invalid channel reference key '{key}'. Expected an integer prefixed with '$' e.g. '$1', '$2', and so on." 167 ) 168 169 if isinstance(value, dict): # Do this for YamlConfigChannelSpec but not str 170 try: 171 _validate_channel(value) 172 except YamlConfigError as err: 173 raise YamlConfigError(f"Rule '{key}' contains an invalid channel reference:\n{err}") 174 175 176class ChannelConfigYamlSpec(TypedDict): 177 """ 178 Formal spec that defines what a channel should look like in YAML. 179 180 `name`: Name of channel. 181 `description`: Optional channel description. 182 `unit`: Unit of measurement. 183 `component`: Name of component that channel belongs to. 184 `data_type`: Type of the data associated with the channel. 185 `enum_types`: Required if `data_type` is `enum. 186 `bit_field_elements`: Required if `data_type` is `bit_field`. 187 """ 188 189 name: str 190 description: NotRequired[str] 191 unit: NotRequired[str] 192 component: NotRequired[str] 193 data_type: Union[ 194 Literal["double"], 195 Literal["string"], 196 Literal["enum"], 197 Literal["bit_field"], 198 Literal["bool"], 199 Literal["float"], 200 Literal["int32"], 201 Literal["int64"], 202 Literal["uint32"], 203 Literal["uint64"], 204 ] 205 enum_types: NotRequired[List[ChannelEnumTypeYamlSpec]] 206 bit_field_elements: NotRequired[List[ChannelBitFieldElementYamlSpec]] 207 208 209class ChannelEnumTypeYamlSpec(TypedDict): 210 """ 211 Formal spec that defines what a channel enum type should look like in YAML. 212 """ 213 214 name: str 215 key: int 216 217 218class ChannelBitFieldElementYamlSpec(TypedDict): 219 """ 220 Formal spec that defines what a bit-field element should look like in YAML. 221 """ 222 223 name: str 224 index: int 225 bit_count: int
class
ChannelConfigYamlSpec(typing_extensions.TypedDict):
177class ChannelConfigYamlSpec(TypedDict): 178 """ 179 Formal spec that defines what a channel should look like in YAML. 180 181 `name`: Name of channel. 182 `description`: Optional channel description. 183 `unit`: Unit of measurement. 184 `component`: Name of component that channel belongs to. 185 `data_type`: Type of the data associated with the channel. 186 `enum_types`: Required if `data_type` is `enum. 187 `bit_field_elements`: Required if `data_type` is `bit_field`. 188 """ 189 190 name: str 191 description: NotRequired[str] 192 unit: NotRequired[str] 193 component: NotRequired[str] 194 data_type: Union[ 195 Literal["double"], 196 Literal["string"], 197 Literal["enum"], 198 Literal["bit_field"], 199 Literal["bool"], 200 Literal["float"], 201 Literal["int32"], 202 Literal["int64"], 203 Literal["uint32"], 204 Literal["uint64"], 205 ] 206 enum_types: NotRequired[List[ChannelEnumTypeYamlSpec]] 207 bit_field_elements: NotRequired[List[ChannelBitFieldElementYamlSpec]]
Formal spec that defines what a channel should look like in YAML.
name
: Name of channel.
description
: Optional channel description.
unit
: Unit of measurement.
component
: Name of component that channel belongs to.
data_type
: Type of the data associated with the channel.
enum_types
: Required if data_type
is enum.
bit_field_elements: Required if
data_typeis
bit_field`.
data_type: Union[Literal['double'], Literal['string'], Literal['enum'], Literal['bit_field'], Literal['bool'], Literal['float'], Literal['int32'], Literal['int64'], Literal['uint32'], Literal['uint64']]
enum_types: typing_extensions.NotRequired[typing.List[ChannelEnumTypeYamlSpec]]
bit_field_elements: typing_extensions.NotRequired[typing.List[ChannelBitFieldElementYamlSpec]]
class
ChannelEnumTypeYamlSpec(typing_extensions.TypedDict):
210class ChannelEnumTypeYamlSpec(TypedDict): 211 """ 212 Formal spec that defines what a channel enum type should look like in YAML. 213 """ 214 215 name: str 216 key: int
Formal spec that defines what a channel enum type should look like in YAML.
class
ChannelBitFieldElementYamlSpec(typing_extensions.TypedDict):
219class ChannelBitFieldElementYamlSpec(TypedDict): 220 """ 221 Formal spec that defines what a bit-field element should look like in YAML. 222 """ 223 224 name: str 225 index: int 226 bit_count: int
Formal spec that defines what a bit-field element should look like in YAML.