Coverage for /home/runner/work/zserio/zserio/compiler/extensions/python/runtime/src/zserio/creator.py: 100%
121 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-12-05 10:43 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-12-05 10:43 +0000
1"""
2The module implements creator of zserio object tree based on type info.
3"""
5import typing
6import enum
8from zserio.typeinfo import (
9 TypeInfo,
10 RecursiveTypeInfo,
11 TypeAttribute,
12 MemberInfo,
13 MemberAttribute,
14)
15from zserio.exception import PythonRuntimeException
18class ZserioTreeCreator:
19 """
20 Allows to build zserio object tree defined by the given type info.
21 """
23 def __init__(
24 self,
25 type_info: typing.Union[TypeInfo, RecursiveTypeInfo],
26 *arguments: typing.List[typing.Any],
27 ) -> None:
28 """
29 Constructor.
31 :param type_info: Type info defining the tree.
32 :param arguments: Arguments for type which defines the tree.
33 """
35 self._root_type_info = type_info
36 self._root_arguments = arguments
37 self._member_info_stack: typing.List[MemberInfo] = []
38 self._value_stack: typing.List[typing.Any] = []
39 self._state = ZserioTreeCreator._State.BEFORE_ROOT
41 def begin_root(self) -> None:
42 """
43 Creates the top level compound element and move to state of building its children.
44 """
46 if self._state != ZserioTreeCreator._State.BEFORE_ROOT:
47 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin root in state '{self._state}'!")
49 self._value_stack.append(self._root_type_info.py_type(*self._root_arguments))
50 self._state = ZserioTreeCreator._State.IN_COMPOUND
52 def end_root(self) -> typing.Any:
53 """
54 Finishes building and returns the created tree.
56 :returns: Zserio object tree.
57 :raises PythonRuntimeException: When the creator is not in state of building the root object.
58 """
60 if self._state != ZserioTreeCreator._State.IN_COMPOUND or len(self._value_stack) != 1:
61 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot end root in state '{self._state}'!")
63 self._state = ZserioTreeCreator._State.BEFORE_ROOT
64 return self._value_stack.pop()
66 def begin_array(self, name: str) -> None:
67 """
68 Creates an array field within the current compound.
70 :param name: Name of the array field.
71 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound.
72 """
74 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
75 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin array in state '{self._state}'!")
77 member_info = self._find_member_info(self._get_type_info(), name)
78 if not MemberAttribute.ARRAY_LENGTH in member_info.attributes:
79 raise PythonRuntimeException(
80 "ZserioTreeCreator: Field " f"'{member_info.schema_name}' is not an array!"
81 )
83 self._member_info_stack.append(member_info)
84 self._value_stack.append([])
85 self._state = ZserioTreeCreator._State.IN_ARRAY
87 def end_array(self):
88 """
89 Finishes the array field.
91 :raises PythonRuntimeException: When the creator is not in an array field.
92 """
93 if self._state != ZserioTreeCreator._State.IN_ARRAY:
94 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot end array in state '{self._state}'!")
96 member_info = self._member_info_stack.pop()
97 value = self._value_stack.pop()
98 setattr(
99 self._value_stack[-1],
100 member_info.attributes[MemberAttribute.PROPERTY_NAME],
101 value,
102 )
103 self._state = ZserioTreeCreator._State.IN_COMPOUND
105 def begin_compound(self, name):
106 """
107 Creates a compound field within the current compound.
109 :param name: Name of the compound field.
110 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound.
111 """
113 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
114 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin compound in state '{self._state}'!")
116 member_info = self._find_member_info(self._get_type_info(), name)
118 if MemberAttribute.ARRAY_LENGTH in member_info.attributes:
119 raise PythonRuntimeException(
120 "ZserioTreeCreator: Field " f"'{member_info.schema_name}' is an array!"
121 )
123 if not TypeAttribute.FIELDS in member_info.type_info.attributes:
124 raise PythonRuntimeException(
125 f"ZserioTreeCreator: Field " f"'{member_info.schema_name}' is not a compound!"
126 )
128 compound = self._create_object(member_info, self._value_stack[-1])
130 self._member_info_stack.append(member_info)
131 self._value_stack.append(compound)
132 self._state = ZserioTreeCreator._State.IN_COMPOUND
134 def end_compound(self):
135 """
136 Finishes the compound.
138 :raises PythonRuntimeException: When the creator is not in a compound field.
139 """
141 if self._state != ZserioTreeCreator._State.IN_COMPOUND or not self._member_info_stack:
142 raise PythonRuntimeException(
143 f"ZserioTreeCreator: Cannot end compound in state '{self._state}'"
144 + (", expecting end_root!" if not self._member_info_stack else "!")
145 )
147 if MemberAttribute.ARRAY_LENGTH in self._member_info_stack[-1].attributes:
148 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound, it's an array element!")
150 member_info = self._member_info_stack.pop()
151 value = self._value_stack.pop()
152 setattr(
153 self._value_stack[-1],
154 member_info.attributes[MemberAttribute.PROPERTY_NAME],
155 value,
156 )
158 def set_value(self, name: str, value: typing.Any):
159 """
160 Sets field value within the current compound.
162 :param name: Name of the field.
163 :param value: Value to set.
165 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound.
166 """
168 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
169 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot set value in state '{self._state}'!")
171 member_info = self._find_member_info(self._get_type_info(), name)
173 if value is not None:
174 if MemberAttribute.ARRAY_LENGTH in member_info.attributes:
175 raise PythonRuntimeException(
176 "ZserioTreeCreator: Expecting array in field " f"'{member_info.schema_name}'!"
177 )
179 if not isinstance(value, member_info.type_info.py_type):
180 raise PythonRuntimeException(
181 f"ZserioTreeCreator: Unexpected value type '{type(value)}', "
182 f"expecting '{member_info.type_info.py_type}'!"
183 )
185 setattr(
186 self._value_stack[-1],
187 member_info.attributes[MemberAttribute.PROPERTY_NAME],
188 value,
189 )
191 def get_field_type(self, name: str) -> typing.Union[TypeInfo, RecursiveTypeInfo]:
192 """
193 Gets type info of the expected field.
195 :param name: Field name.
196 :returns: Type info of the expected field.
197 :raises PythonRuntimeException: When the creator is not in a compound.
198 """
200 if self._state != ZserioTreeCreator._State.IN_COMPOUND:
201 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot get field type in state '{self._state}'!")
203 member_info = self._find_member_info(self._get_type_info(), name)
204 return member_info.type_info
206 def begin_compound_element(self):
207 """
208 Creates compound array element within the current array.
210 :raises PythonRuntimeException: When the creator is not in an array of compounds.
211 """
213 if self._state != ZserioTreeCreator._State.IN_ARRAY:
214 raise PythonRuntimeException(
215 "ZserioTreeCreator: Cannot begin compound element in state " f"'{self._state}'!"
216 )
218 member_info = self._member_info_stack[-1]
220 if not TypeAttribute.FIELDS in member_info.type_info.attributes:
221 raise PythonRuntimeException(
222 f"ZserioTreeCreator: Field " f"'{self._member_info_stack[-1].schema_name}' is not a compound!"
223 )
225 compound = self._create_object(member_info, self._value_stack[-2], len(self._value_stack[-1]))
227 self._value_stack.append(compound)
228 self._state = ZserioTreeCreator._State.IN_COMPOUND
230 def end_compound_element(self) -> None:
231 """
232 Finishes the compound element.
234 :raises PythonRuntimeException: When the creator is not in a compound element.
235 """
237 if self._state != ZserioTreeCreator._State.IN_COMPOUND or not self._member_info_stack:
238 raise PythonRuntimeException(
239 "ZserioTreeCreator: Cannot end compound element in state "
240 f"'{self._state}'" + (", expecting end_root!" if not self._member_info_stack else "!")
241 )
243 if not MemberAttribute.ARRAY_LENGTH in self._member_info_stack[-1].attributes:
244 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound element, not in array!")
246 value = self._value_stack.pop()
247 self._value_stack[-1].append(value)
248 self._state = ZserioTreeCreator._State.IN_ARRAY
250 def add_value_element(self, value: typing.Any) -> None:
251 """
252 Adds the value to the array.
254 :param value: Value to add.
255 :raises PythonRuntimeException: When the creator is not in an array of simple values.
256 """
258 if self._state != ZserioTreeCreator._State.IN_ARRAY:
259 raise PythonRuntimeException(
260 "ZserioTreeCreator: Cannot add value element in state " f"'{self._state}'!"
261 )
263 element_type_info = self._member_info_stack[-1].type_info
264 if value is not None and not isinstance(value, element_type_info.py_type):
265 raise PythonRuntimeException(
266 f"ZserioTreeCreator: Unexpected value type '{type(value)}', expecting "
267 f"'{element_type_info.py_type}'!"
268 )
270 self._value_stack[-1].append(value)
272 def get_element_type(self) -> typing.Union[TypeInfo, RecursiveTypeInfo]:
273 """
274 Gets type info of the expected array element.
276 :returns: Type info of the expected array element.
277 :raises PythonRuntimeException: When the creator is not in an array.
278 """
280 if self._state != ZserioTreeCreator._State.IN_ARRAY:
281 raise PythonRuntimeException("ZserioTreeCreator: Cannot get element type in state '{self._state}'!")
283 member_info = self._member_info_stack[-1]
284 return member_info.type_info
286 def _get_type_info(self) -> typing.Union[TypeInfo, RecursiveTypeInfo]:
287 return self._member_info_stack[-1].type_info if self._member_info_stack else self._root_type_info
289 @staticmethod
290 def _find_member_info(type_info: typing.Union[TypeInfo, RecursiveTypeInfo], name: str) -> MemberInfo:
291 members = type_info.attributes[TypeAttribute.FIELDS]
292 for member in members:
293 if member.schema_name == name:
294 return member
295 raise PythonRuntimeException(
296 f"ZserioTreeCreator: Field '{name}' not found in " f"'{type_info.schema_name}'!"
297 )
299 @staticmethod
300 def _create_object(
301 member_info: MemberInfo,
302 parent: typing.Any,
303 element_index: typing.Optional[int] = None,
304 ) -> typing.Any:
305 args = []
306 if MemberAttribute.TYPE_ARGUMENTS in member_info.attributes:
307 for argument_lambda in member_info.attributes[MemberAttribute.TYPE_ARGUMENTS]:
308 args.append(argument_lambda(parent, element_index))
310 return member_info.type_info.py_type(*args)
312 class _State(enum.Enum):
313 BEFORE_ROOT = enum.auto()
314 IN_COMPOUND = enum.auto()
315 IN_ARRAY = enum.auto()