Coverage for /home/jenkins/workspace/NDS/Zserio/NDS_ZSERIO-linux-build/compiler/extensions/python/runtime/src/zserio/creator.py: 100%
121 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-12-13 15:12 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-12-13 15:12 +0000
1"""
2The module implements creator of zserio object tree based on type info.
3"""
5import typing
6import enum
8from zserio.typeinfo import TypeInfo, RecursiveTypeInfo, TypeAttribute, MemberInfo, MemberAttribute
9from zserio.exception import PythonRuntimeException
11class ZserioTreeCreator:
12 """
13 Allows to build zserio object tree defined by the given type info.
14 """
16 def __init__(self, type_info: typing.Union[TypeInfo, RecursiveTypeInfo],
17 *arguments: typing.List[typing.Any]) -> None:
18 """
19 Constructor.
21 :param type_info: Type info defining the tree.
22 :param arguments: Arguments for type which defines the tree.
23 """
25 self._root_type_info = type_info
26 self._root_arguments = arguments
27 self._member_info_stack: typing.List[MemberInfo] = []
28 self._value_stack: typing.List[typing.Any] = []
29 self._state = ZserioTreeCreator._State.BEFORE_ROOT
31 def begin_root(self) -> None:
32 """
33 Creates the top level compound element and move to state of building its children.
34 """
36 if self._state != ZserioTreeCreator._State.BEFORE_ROOT:
37 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin root in state '{self._state}'!")
39 self._value_stack.append(self._root_type_info.py_type(*self._root_arguments))
40 self._state = ZserioTreeCreator._State.IN_COMPOUND
42 def end_root(self) -> typing.Any:
43 """
44 Finishes building and returns the created tree.
46 :returns: Zserio object tree.
47 :raises PythonRuntimeException: When the creator is not in state of building the root object.
48 """
50 if self._state != ZserioTreeCreator._State.IN_COMPOUND or len(self._value_stack) != 1:
51 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot end root in state '{self._state}'!")
53 self._state = ZserioTreeCreator._State.BEFORE_ROOT
54 return self._value_stack.pop()
56 def begin_array(self, name: str) -> None:
57 """
58 Creates an array field within the current compound.
60 :param name: Name of the array field.
61 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound.
62 """
64 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
65 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin array in state '{self._state}'!")
67 member_info = self._find_member_info(self._get_type_info(), name)
68 if not MemberAttribute.ARRAY_LENGTH in member_info.attributes:
69 raise PythonRuntimeException("ZserioTreeCreator: Field "
70 f"'{member_info.schema_name}' is not an array!")
72 self._member_info_stack.append(member_info)
73 self._value_stack.append([])
74 self._state = ZserioTreeCreator._State.IN_ARRAY
76 def end_array(self):
77 """
78 Finishes the array field.
80 :raises PythonRuntimeException: When the creator is not in an array field.
81 """
82 if self._state != ZserioTreeCreator._State.IN_ARRAY:
83 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot end array in state '{self._state}'!")
85 member_info = self._member_info_stack.pop()
86 value = self._value_stack.pop()
87 setattr(self._value_stack[-1], member_info.attributes[MemberAttribute.PROPERTY_NAME], value)
88 self._state = ZserioTreeCreator._State.IN_COMPOUND
90 def begin_compound(self, name):
91 """
92 Creates a compound field within the current compound.
94 :param name: Name of the compound field.
95 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound.
96 """
98 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
99 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin compound in state '{self._state}'!")
101 member_info = self._find_member_info(self._get_type_info(), name)
103 if MemberAttribute.ARRAY_LENGTH in member_info.attributes:
104 raise PythonRuntimeException("ZserioTreeCreator: Field "
105 f"'{member_info.schema_name}' is an array!")
107 if not TypeAttribute.FIELDS in member_info.type_info.attributes:
108 raise PythonRuntimeException(f"ZserioTreeCreator: Field "
109 f"'{member_info.schema_name}' is not a compound!")
111 compound = self._create_object(member_info, self._value_stack[-1])
113 self._member_info_stack.append(member_info)
114 self._value_stack.append(compound)
115 self._state = ZserioTreeCreator._State.IN_COMPOUND
117 def end_compound(self):
118 """
119 Finishes the compound.
121 :raises PythonRuntimeException: When the creator is not in a compound field.
122 """
124 if self._state != ZserioTreeCreator._State.IN_COMPOUND or not self._member_info_stack:
125 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot end compound in state '{self._state}'" +
126 (", expecting end_root!" if not self._member_info_stack else "!"))
128 if MemberAttribute.ARRAY_LENGTH in self._member_info_stack[-1].attributes:
129 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound, it's an array element!")
131 member_info = self._member_info_stack.pop()
132 value = self._value_stack.pop()
133 setattr(self._value_stack[-1], member_info.attributes[MemberAttribute.PROPERTY_NAME], value)
135 def set_value(self, name: str, value: typing.Any):
136 """
137 Sets field value within the current compound.
139 :param name: Name of the field.
140 :param value: Value to set.
142 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound.
143 """
145 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
146 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot set value in state '{self._state}'!")
148 member_info = self._find_member_info(self._get_type_info(), name)
150 if value is not None:
151 if MemberAttribute.ARRAY_LENGTH in member_info.attributes:
152 raise PythonRuntimeException("ZserioTreeCreator: Expecting array in field "
153 f"'{member_info.schema_name}'!")
155 if not isinstance(value, member_info.type_info.py_type):
156 raise PythonRuntimeException(f"ZserioTreeCreator: Unexpected value type '{type(value)}', "
157 f"expecting '{member_info.type_info.py_type}'!")
159 setattr(self._value_stack[-1], member_info.attributes[MemberAttribute.PROPERTY_NAME], value)
161 def get_field_type(self, name: str) -> typing.Union[TypeInfo, RecursiveTypeInfo]:
162 """
163 Gets type info of the expected field.
165 :param name: Field name.
166 :returns: Type info of the expected field.
167 :raises PythonRuntimeException: When the creator is not in a compound.
168 """
170 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
171 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot get field type in state '{self._state}'!")
173 member_info = self._find_member_info(self._get_type_info(), name)
174 return member_info.type_info
176 def begin_compound_element(self):
177 """
178 Creates compound array element within the current array.
180 :raises PythonRuntimeException: When the creator is not in an array of compounds.
181 """
183 if self._state != ZserioTreeCreator._State.IN_ARRAY:
184 raise PythonRuntimeException("ZserioTreeCreator: Cannot begin compound element in state "
185 f"'{self._state}'!")
187 member_info = self._member_info_stack[-1]
189 if not TypeAttribute.FIELDS in member_info.type_info.attributes:
190 raise PythonRuntimeException(f"ZserioTreeCreator: Field "
191 f"'{self._member_info_stack[-1].schema_name}' is not a compound!")
193 compound = self._create_object(member_info, self._value_stack[-2], len(self._value_stack[-1]))
195 self._value_stack.append(compound)
196 self._state = ZserioTreeCreator._State.IN_COMPOUND
198 def end_compound_element(self) -> None:
199 """
200 Finishes the compound element.
202 :raises PythonRuntimeException: When the creator is not in a compound element.
203 """
205 if self._state != ZserioTreeCreator._State.IN_COMPOUND or not self._member_info_stack:
206 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound element in state "
207 f"'{self._state}'" +
208 (", expecting end_root!" if not self._member_info_stack else "!"))
210 if not MemberAttribute.ARRAY_LENGTH in self._member_info_stack[-1].attributes:
211 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound element, not in array!")
213 value = self._value_stack.pop()
214 self._value_stack[-1].append(value)
215 self._state = ZserioTreeCreator._State.IN_ARRAY
217 def add_value_element(self, value: typing.Any) -> None:
218 """
219 Adds the value to the array.
221 :param value: Value to add.
222 :raises PythonRuntimeException: When the creator is not in an array of simple values.
223 """
225 if self._state != ZserioTreeCreator._State.IN_ARRAY:
226 raise PythonRuntimeException("ZserioTreeCreator: Cannot add value element in state "
227 f"'{self._state}'!")
229 element_type_info = self._member_info_stack[-1].type_info
230 if value is not None and not isinstance(value, element_type_info.py_type):
231 raise PythonRuntimeException(f"ZserioTreeCreator: Unexpected value type '{type(value)}', expecting "
232 f"'{element_type_info.py_type}'!")
234 self._value_stack[-1].append(value)
236 def get_element_type(self) -> typing.Union[TypeInfo, RecursiveTypeInfo]:
237 """
238 Gets type info of the expected array element.
240 :returns: Type info of the expected array element.
241 :raises PythonRuntimeException: When the creator is not in an array.
242 """
244 if self._state != ZserioTreeCreator._State.IN_ARRAY:
245 raise PythonRuntimeException("ZserioTreeCreator: Cannot get element type in state '{self._state}'!")
247 member_info = self._member_info_stack[-1]
248 return member_info.type_info
250 def _get_type_info(self) -> typing.Union[TypeInfo, RecursiveTypeInfo]:
251 return self._member_info_stack[-1].type_info if self._member_info_stack else self._root_type_info
253 @staticmethod
254 def _find_member_info(type_info: typing.Union[TypeInfo, RecursiveTypeInfo], name: str) -> MemberInfo:
255 members = type_info.attributes[TypeAttribute.FIELDS]
256 for member in members:
257 if member.schema_name == name:
258 return member
259 raise PythonRuntimeException(f"ZserioTreeCreator: Field '{name}' not found in "
260 f"'{type_info.schema_name}'!")
262 @staticmethod
263 def _create_object(member_info: MemberInfo, parent: typing.Any,
264 element_index: typing.Optional[int]=None) -> typing.Any:
265 args = []
266 if MemberAttribute.TYPE_ARGUMENTS in member_info.attributes:
267 for argument_lambda in member_info.attributes[MemberAttribute.TYPE_ARGUMENTS]:
268 args.append(argument_lambda(parent, element_index))
270 return member_info.type_info.py_type(*args)
272 class _State(enum.Enum):
273 BEFORE_ROOT = enum.auto()
274 IN_COMPOUND = enum.auto()
275 IN_ARRAY = enum.auto()