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

1""" 

2The module implements creator of zserio object tree based on type info. 

3""" 

4 

5import typing 

6import enum 

7 

8from zserio.typeinfo import TypeInfo, RecursiveTypeInfo, TypeAttribute, MemberInfo, MemberAttribute 

9from zserio.exception import PythonRuntimeException 

10 

11class ZserioTreeCreator: 

12 """ 

13 Allows to build zserio object tree defined by the given type info. 

14 """ 

15 

16 def __init__(self, type_info: typing.Union[TypeInfo, RecursiveTypeInfo], 

17 *arguments: typing.List[typing.Any]) -> None: 

18 """ 

19 Constructor. 

20 

21 :param type_info: Type info defining the tree. 

22 :param arguments: Arguments for type which defines the tree. 

23 """ 

24 

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 

30 

31 def begin_root(self) -> None: 

32 """ 

33 Creates the top level compound element and move to state of building its children. 

34 """ 

35 

36 if self._state != ZserioTreeCreator._State.BEFORE_ROOT: 

37 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin root in state '{self._state}'!") 

38 

39 self._value_stack.append(self._root_type_info.py_type(*self._root_arguments)) 

40 self._state = ZserioTreeCreator._State.IN_COMPOUND 

41 

42 def end_root(self) -> typing.Any: 

43 """ 

44 Finishes building and returns the created tree. 

45 

46 :returns: Zserio object tree. 

47 :raises PythonRuntimeException: When the creator is not in state of building the root object. 

48 """ 

49 

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}'!") 

52 

53 self._state = ZserioTreeCreator._State.BEFORE_ROOT 

54 return self._value_stack.pop() 

55 

56 def begin_array(self, name: str) -> None: 

57 """ 

58 Creates an array field within the current compound. 

59 

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 """ 

63 

64 if self._state != ZserioTreeCreator._State.IN_COMPOUND: 

65 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin array in state '{self._state}'!") 

66 

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!") 

71 

72 self._member_info_stack.append(member_info) 

73 self._value_stack.append([]) 

74 self._state = ZserioTreeCreator._State.IN_ARRAY 

75 

76 def end_array(self): 

77 """ 

78 Finishes the array field. 

79 

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}'!") 

84 

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 

89 

90 def begin_compound(self, name): 

91 """ 

92 Creates a compound field within the current compound. 

93 

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 """ 

97 

98 if self._state != ZserioTreeCreator._State.IN_COMPOUND: 

99 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot begin compound in state '{self._state}'!") 

100 

101 member_info = self._find_member_info(self._get_type_info(), name) 

102 

103 if MemberAttribute.ARRAY_LENGTH in member_info.attributes: 

104 raise PythonRuntimeException("ZserioTreeCreator: Field " 

105 f"'{member_info.schema_name}' is an array!") 

106 

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!") 

110 

111 compound = self._create_object(member_info, self._value_stack[-1]) 

112 

113 self._member_info_stack.append(member_info) 

114 self._value_stack.append(compound) 

115 self._state = ZserioTreeCreator._State.IN_COMPOUND 

116 

117 def end_compound(self): 

118 """ 

119 Finishes the compound. 

120 

121 :raises PythonRuntimeException: When the creator is not in a compound field. 

122 """ 

123 

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 "!")) 

127 

128 if MemberAttribute.ARRAY_LENGTH in self._member_info_stack[-1].attributes: 

129 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound, it's an array element!") 

130 

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) 

134 

135 def set_value(self, name: str, value: typing.Any): 

136 """ 

137 Sets field value within the current compound. 

138 

139 :param name: Name of the field. 

140 :param value: Value to set. 

141 

142 :raises PythonRuntimeException: When the field doesn't exist or when the creator is not in a compound. 

143 """ 

144 

145 if self._state != ZserioTreeCreator._State.IN_COMPOUND: 

146 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot set value in state '{self._state}'!") 

147 

148 member_info = self._find_member_info(self._get_type_info(), name) 

149 

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}'!") 

154 

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}'!") 

158 

159 setattr(self._value_stack[-1], member_info.attributes[MemberAttribute.PROPERTY_NAME], value) 

160 

161 def get_field_type(self, name: str) -> typing.Union[TypeInfo, RecursiveTypeInfo]: 

162 """ 

163 Gets type info of the expected field. 

164 

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 """ 

169 

170 if self._state != ZserioTreeCreator._State.IN_COMPOUND: 

171 raise PythonRuntimeException(f"ZserioTreeCreator: Cannot get field type in state '{self._state}'!") 

172 

173 member_info = self._find_member_info(self._get_type_info(), name) 

174 return member_info.type_info 

175 

176 def begin_compound_element(self): 

177 """ 

178 Creates compound array element within the current array. 

179 

180 :raises PythonRuntimeException: When the creator is not in an array of compounds. 

181 """ 

182 

183 if self._state != ZserioTreeCreator._State.IN_ARRAY: 

184 raise PythonRuntimeException("ZserioTreeCreator: Cannot begin compound element in state " 

185 f"'{self._state}'!") 

186 

187 member_info = self._member_info_stack[-1] 

188 

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!") 

192 

193 compound = self._create_object(member_info, self._value_stack[-2], len(self._value_stack[-1])) 

194 

195 self._value_stack.append(compound) 

196 self._state = ZserioTreeCreator._State.IN_COMPOUND 

197 

198 def end_compound_element(self) -> None: 

199 """ 

200 Finishes the compound element. 

201 

202 :raises PythonRuntimeException: When the creator is not in a compound element. 

203 """ 

204 

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 "!")) 

209 

210 if not MemberAttribute.ARRAY_LENGTH in self._member_info_stack[-1].attributes: 

211 raise PythonRuntimeException("ZserioTreeCreator: Cannot end compound element, not in array!") 

212 

213 value = self._value_stack.pop() 

214 self._value_stack[-1].append(value) 

215 self._state = ZserioTreeCreator._State.IN_ARRAY 

216 

217 def add_value_element(self, value: typing.Any) -> None: 

218 """ 

219 Adds the value to the array. 

220 

221 :param value: Value to add. 

222 :raises PythonRuntimeException: When the creator is not in an array of simple values. 

223 """ 

224 

225 if self._state != ZserioTreeCreator._State.IN_ARRAY: 

226 raise PythonRuntimeException("ZserioTreeCreator: Cannot add value element in state " 

227 f"'{self._state}'!") 

228 

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}'!") 

233 

234 self._value_stack[-1].append(value) 

235 

236 def get_element_type(self) -> typing.Union[TypeInfo, RecursiveTypeInfo]: 

237 """ 

238 Gets type info of the expected array element. 

239 

240 :returns: Type info of the expected array element. 

241 :raises PythonRuntimeException: When the creator is not in an array. 

242 """ 

243 

244 if self._state != ZserioTreeCreator._State.IN_ARRAY: 

245 raise PythonRuntimeException("ZserioTreeCreator: Cannot get element type in state '{self._state}'!") 

246 

247 member_info = self._member_info_stack[-1] 

248 return member_info.type_info 

249 

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 

252 

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}'!") 

261 

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)) 

269 

270 return member_info.type_info.py_type(*args) 

271 

272 class _State(enum.Enum): 

273 BEFORE_ROOT = enum.auto() 

274 IN_COMPOUND = enum.auto() 

275 IN_ARRAY = enum.auto()