Coverage for /home/runner/work/zserio/zserio/compiler/extensions/python/runtime/src/zserio/typeinfo.py: 100%

118 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2024-07-18 11:41 +0000

1""" 

2The module contains classes for type info. 

3""" 

4 

5import typing 

6import enum 

7 

8 

9class TypeInfo: 

10 """ 

11 Type info class which provides information about generated types. 

12 """ 

13 

14 def __init__( 

15 self, 

16 schema_name: str, 

17 py_type: typing.Type, 

18 *, 

19 attributes: typing.Dict["TypeAttribute", typing.Any] = None 

20 ): 

21 """ 

22 Type info constructor. 

23 

24 :param schema_name: Zserio schema full type name. 

25 :param py_type: Reference to the generated type. 

26 :param attributes: List of type attributes. 

27 """ 

28 

29 self._schema_name = schema_name 

30 self._py_type = py_type 

31 self._attributes = attributes if attributes is not None else {} 

32 

33 @property 

34 def schema_name(self) -> str: 

35 """ 

36 Returns the full type name as is defined in Zserio schema. 

37 

38 :returns: Zserio schema full type name. 

39 """ 

40 

41 return self._schema_name 

42 

43 @property 

44 def py_type(self) -> typing.Type: 

45 """ 

46 Gets Python type generated for this Zserio type. 

47 

48 :returns: Python type. 

49 """ 

50 

51 return self._py_type 

52 

53 @property 

54 def attributes(self) -> typing.Dict["TypeAttribute", typing.Any]: 

55 """ 

56 Gets dictionary with type attributes. 

57 

58 Attribute is a an arbitrary value which type is given by the key, which is TypeAttribute enumeration. 

59 

60 * `(TypeAttribute.UNDERLYING_TYPE, TypeInfo(...))` 

61 

62 * denotes that the type has an underlying type (e.g. enum or bitmask), 

63 the value is a TypeInfo of the underlying type 

64 

65 * `(TypeAttribute.UNDERLYING_TYPE_ARGUMENTS, [lambda: 5])` 

66 

67 * keeps type arguments of the underlying type when it is a dynamic bit field, 

68 the value is a lambda function which returns the argument constant value 

69 

70 * `(TypeAttribute.ENUM_ITEMS, [ItemInfo(...), ItemInfo(...), ...])` 

71 

72 * denotes that the type is an enumeration, the value contains list of enum items ItemInfo 

73 

74 * `(TypeAttribute.BITMASK_VALUES, [ItemInfo(...), ItemInfo(...), ...])` 

75 

76 * denotes that the type is a bitmask, the value contains list of bitmask values ItemInfo 

77 

78 * `(TypeAttribute.FIELDS, [MemberInfo(...), MemberInfo(...), ...])` 

79 

80 * denotes that the type is a compound type, the value contains list of fields MemberInfo, 

81 the attribute is present even for empty compounds and then it contains the empty list 

82 

83 * `(TypeAttribute.PARAMETERS, [MemberInfo(...), MemberInfo(...), ...])` 

84 

85 * denotes that the compound type is parameterized type, the value contains non-empty list of 

86 parameters MemberInfo, for non-parameterized types the attribute is not present 

87 

88 * `(TypeAttribute.FUNCTIONS, [MemberInfo(...), MemberInfo(...), ...])` 

89 

90 * denotes that the compound type has functions, the value contains non-empty list of functions 

91 MemberInfo, for compounds without functions the attribute is not present 

92 

93 * `(TypeAttribute.SELECTOR, None`) `(TypeAttribute.SELECTOR, lambda self: self.param1)` 

94 

95 * denotes that the type is either a union (when the value is None) or choice when the 

96 value contains the selector expression as a lambda function taking single parent argument 

97 

98 * `(TypeAttribute.CASES, [CaseInfo(...), CaseInfo(...), ...])` 

99 

100 * denotes that the type is a choice, the value contains list of CaseInfo for each choice case 

101 * note that the TypeAttribute.FIELDS attribute is present also in choices 

102 

103 * `(TypeAttribute.TEMPLATE_NAME, 'TemplatedStructure')` 

104 

105 * denotes that the type is a template instantiation, the value contains the template name 

106 

107 * `(TypeAttribute.TEMPLATE_ARGUMENTS, [test.TemplateArg.type_info(), ...])` 

108 

109 * present when the type is a template instantiation, the value contains list of template arguments 

110 TypeInfo 

111 

112 * `(TypeAttribute.COLUMNS, [MemberInfo(...), MemberInfo(...), ...])` 

113 

114 * denotes that the type is a SQL table, the value contains list of columns MemberInfo 

115 

116 * `(TypeAttribute.TABLES, [MemberInfo(...), MemberInfo(...), ...])` 

117 

118 * denotes that the type is a SQL database, the value contain list of tables MemberInfo 

119 

120 * `(TypeAttribute.SQL_CONSTRAINT, 'PRIMARY KEY(columnA)')` 

121 

122 * denotes that the SQL table contains a SQL constraint 

123 

124 * `(TypeAttribute.VIRTUAL_TABLE_USING, 'fts4')` 

125 

126 * denotes that the SQL table is a virtual table, the value contains the used virtual table module 

127 

128 * `(TypeAttribute.WITHOUT_ROWID, None)` 

129 

130 * denotes that the SQL table is a WITHOUT ROWID table, the value is always None 

131 

132 * `(TypeAttribute.MESSAGES, [MemberInfo(...), MemberInfo(...), ...])` 

133 

134 * denotes that the type is a pub-sub, the value contains list of messages MemberInfo 

135 

136 * `(TypeAttribute.METHODS, [MemberInfo(...), MemberInfo(...), ...])` 

137 

138 * denotes that the type is a service, the value contains list of methods MemberInfo 

139 

140 :returns Type attributes. 

141 """ 

142 

143 return self._attributes 

144 

145 

146class RecursiveTypeInfo: 

147 """ 

148 Type info for recursive types used as a wrapper around generated static type_info method to prevent 

149 infinite recursion in type info definition. 

150 """ 

151 

152 def __init__(self, type_info_func: typing.Callable[[], TypeInfo]): 

153 """ 

154 Constructor. 

155 

156 :param type_info_func: Generated static type_info method to wrap. 

157 """ 

158 

159 self._type_info_func = type_info_func 

160 self._type_info = None 

161 

162 @property 

163 def schema_name(self) -> str: 

164 """ 

165 See :py:attr:`TypeInfo.schema_name`. 

166 """ 

167 

168 return self._get_type_info().schema_name 

169 

170 @property 

171 def py_type(self) -> typing.Type: 

172 """ 

173 See :py:attr:`TypeInfo.py_type`. 

174 """ 

175 

176 return self._get_type_info().py_type 

177 

178 @property 

179 def attributes(self) -> typing.Dict["TypeAttribute", typing.Any]: 

180 """ 

181 See :py:attr:`TypeInfo.attributes`. 

182 """ 

183 

184 return self._get_type_info().attributes 

185 

186 def _get_type_info(self): 

187 if self._type_info is None: 

188 self._type_info = self._type_info_func() 

189 return self._type_info 

190 

191 

192class TypeAttribute(enum.Enum): 

193 """ 

194 Type attribute type to be used in TypeInfo. 

195 

196 Determines type of the second element in the attribute tuple returned in attributes list from TypeInfo. 

197 """ 

198 

199 UNDERLYING_TYPE = enum.auto() 

200 UNDERLYING_TYPE_ARGUMENTS = enum.auto() 

201 ENUM_ITEMS = enum.auto() 

202 BITMASK_VALUES = enum.auto() 

203 FIELDS = enum.auto() 

204 PARAMETERS = enum.auto() 

205 FUNCTIONS = enum.auto() 

206 SELECTOR = enum.auto() 

207 CASES = enum.auto() 

208 TEMPLATE_NAME = enum.auto() 

209 TEMPLATE_ARGUMENTS = enum.auto() 

210 COLUMNS = enum.auto() 

211 TABLES = enum.auto() 

212 SQL_CONSTRAINT = enum.auto() 

213 VIRTUAL_TABLE_USING = enum.auto() 

214 WITHOUT_ROWID = enum.auto() 

215 MESSAGES = enum.auto() 

216 METHODS = enum.auto() 

217 

218 

219class MemberInfo: 

220 """ 

221 Member info class which provides information about members of compound types. 

222 """ 

223 

224 def __init__( 

225 self, 

226 schema_name: str, 

227 typeinfo: typing.Union[TypeInfo, RecursiveTypeInfo], 

228 *, 

229 attributes: typing.Dict["MemberAttribute", typing.Any] = None 

230 ): 

231 """ 

232 Member info constructor. 

233 

234 :param schema_name: Name of the member as is defined in Zserio schema. 

235 :param type_info: Type info of the member. 

236 :param attributes: List of member attributes. 

237 """ 

238 

239 self._schema_name = schema_name 

240 self._type_info = typeinfo 

241 self._attributes = attributes if attributes is not None else {} 

242 

243 @property 

244 def schema_name(self) -> str: 

245 """ 

246 Gets name of the member as is defined in Zserio schema. 

247 

248 :returns: Member name in Zserio schema. 

249 """ 

250 

251 return self._schema_name 

252 

253 @property 

254 def type_info(self) -> typing.Union[TypeInfo, RecursiveTypeInfo]: 

255 """ 

256 Gets type info of this member. 

257 

258 :returns: Type info. 

259 """ 

260 

261 return self._type_info 

262 

263 @property 

264 def attributes(self) -> typing.Dict["MemberAttribute", typing.Any]: 

265 """ 

266 Gets dictionary with member attributes. 

267 

268 Attribute is a an arbitrary value which type is given by the key, which is MemberAttribute enumeration. 

269 All expressions are stored as strings. 

270 

271 **Possible attributes:** 

272 

273 * `(MemberAttribute.PROPERTY_NAME, 'field1')` 

274 

275 * contains name of the property generated in Python 

276 

277 * `(MemberAttribute.TYPE_ARGUMENTS, [(lambda self, zserio_index: self.field1), ...])` 

278 

279 * for compound type members, keeps type arguments for parameterized types or dynamic bit fields, 

280 the value contains list of lambda functions evaluating particular arguments expression, 

281 where the lambdas take parent and an element index (which can be None if not used) as arguments 

282 

283 * for members of sql tables, keeps type arguments for columns, the value contains list of 

284 lambdas where the lambdas take either single explicit parameter argument for explicit parameters or 

285 single 'self' argument, which is an object providing property-like getters for column names 

286 used in expressions 

287 

288 * `(MemberAttribute.EXTENDED, None)` 

289 

290 * denotes that the member field is extended, the value is always None 

291 

292 * `(MemberAttribute.ALIGN, lambda: 8)` 

293 

294 * denotes that the member field has an alignment, the value is a lambda function which returns the 

295 alignment constant value 

296 

297 * `(MemberAttribute.OFFSET, lambda self: self.offset_field)` 

298 

299 * denotes that the member field has an offset, the value contains the offset expression 

300 as a lambda function taking single parent argument 

301 

302 * `(MemberAttribute.INITIALIZER, lambda: 10)` 

303 

304 * denotes that the member field has an initializer, the value is a lambda function which returns the 

305 the initializer constant value 

306 

307 * `(MemberAttribute.OPTIONAL, None)`, `(MemberAttribute.OPTIONAL, lambda self: self.field1 != 0)` 

308 

309 * denotes that the member is an optional, when the value is None, then it's an auto optional, 

310 otherwise it contains the optional clause as a lambda function taking single parent argument 

311 

312 * `(MemberAttribute.IS_USED_INDICATOR_NAME, 'is_field_used)` 

313 

314 * if the member is an optional, the value contains the "is_used" indicator name generated in Python 

315 

316 * `(MemberAttribute.IS_SET_INDICATOR_NAME, 'is_field_set)` 

317 

318 * if the member is an optional, the value contains the "is_set" indicator name generated in Python 

319 

320 * `(MemberAttribute.CONSTRAINT, lambda self: field > 10)` 

321 

322 * denotes that the member has a constraint, the value contains the constraint expression 

323 as a lambda function taking single parent argument 

324 

325 * `(MemberAttribute.FUNCTION_NAME, 'function_name')` 

326 

327 * keeps the generated function name 

328 

329 * `MemberAttribute.FUNCTION_RESULT, lambda self: self.field1 + 5)` 

330 

331 * keeps the result expression of a function as a lambda function taking single parent argument 

332 

333 * `(MemberAttribute.ARRAY_LENGTH, None)`, `(MemberAttribute.ARRAY_LENGTH, lambda self: self.field1 + 1)` 

334 

335 * denotes that the member is an array, when the value is None, then it's an auto array, 

336 otherwise it contains the length expression as a lambda function taking single parent argument 

337 

338 * `(MemberAttribute.IMPLICIT, None)` 

339 

340 * denotes that the member is an implicit array, the value is always None 

341 

342 * `(MemberAttribute.PACKED, None)` 

343 

344 * denotes that the member is a packed array, the value is always None 

345 

346 * `(MemberAttribute.SQL_TYPE_NAME, 'INTEGER')` 

347 

348 * keeps SQLite type name used for this column 

349 

350 * `(MemberAttribute.SQL_CONSTRAINT, 'PRIMARY KEY NOT NULL')` 

351 

352 * denotes that the member has a SQL constraint 

353 

354 * `(MemberAttribute.VIRTUAL, None)` 

355 

356 * denotes that the column in a SQL table is virtual 

357 

358 * `(MemberAttribute.TOPIC, 'topic/definition')` 

359 

360 * keeps the topic definition of a pub-sub message 

361 

362 * `(MemberAttribute.PUBLISH, 'publish_message_name')` 

363 

364 * denotes that the pub-sub message is published, the value contains the publishing method name 

365 

366 * `(MemberAttribute.SUBSCRIBE, 'subscribe_message_name')` 

367 

368 * denotes that the pub-sub message is subscribed, the value contains the subscribing method name 

369 

370 * `(MemberAttribute.CLIENT_METHOD_NAME, 'client_method_name')` 

371 

372 * keeps the name of the method in the generated Client class 

373 

374 * `(MemberAttribute.REQUEST_TYPE, request_type.type_info())` 

375 

376 * keeps the request type TypeInfo, note that response type is in the method TypeInfo 

377 

378 :returns: Member attributes. 

379 """ 

380 

381 return self._attributes 

382 

383 

384class MemberAttribute(enum.Enum): 

385 """ 

386 Member attribute type to be used in MemberInfo. 

387 

388 Determines type of the second element in the attribute tuple returned in attributes list from MemberInfo. 

389 """ 

390 

391 PROPERTY_NAME = enum.auto() 

392 TYPE_ARGUMENTS = enum.auto() 

393 EXTENDED = enum.auto() 

394 ALIGN = enum.auto() 

395 OFFSET = enum.auto() 

396 INITIALIZER = enum.auto() 

397 OPTIONAL = enum.auto() 

398 IS_USED_INDICATOR_NAME = enum.auto() 

399 IS_SET_INDICATOR_NAME = enum.auto() 

400 CONSTRAINT = enum.auto() 

401 FUNCTION_NAME = enum.auto() 

402 FUNCTION_RESULT = enum.auto() 

403 ARRAY_LENGTH = enum.auto() 

404 IMPLICIT = enum.auto() 

405 PACKED = enum.auto() 

406 SQL_TYPE_NAME = enum.auto() 

407 SQL_CONSTRAINT = enum.auto() 

408 VIRTUAL = enum.auto() 

409 TOPIC = enum.auto() 

410 PUBLISH = enum.auto() 

411 SUBSCRIBE = enum.auto() 

412 CLIENT_METHOD_NAME = enum.auto() 

413 REQUEST_TYPE = enum.auto() 

414 

415 

416class CaseInfo: 

417 """ 

418 Case info class which provides information about choice cases in generated choices. 

419 """ 

420 

421 def __init__( 

422 self, 

423 case_expressions: typing.List[typing.Any], 

424 field: typing.Optional[MemberInfo], 

425 ): 

426 """ 

427 Constructor. 

428 

429 :param case_expressions: List of case expression in the choice case. When empty, it's a default case. 

430 :param field: Field associated with the choice case, can be empty. 

431 """ 

432 

433 self._case_expressions = case_expressions 

434 self._field = field 

435 

436 @property 

437 def case_expressions(self) -> typing.List[typing.Any]: 

438 """ 

439 Gets case expressions in the choice case. An empty list denotes the default case. 

440 

441 :returns: List of case expressions as evaluated constant values. 

442 """ 

443 

444 return self._case_expressions 

445 

446 @property 

447 def field(self) -> typing.Optional[MemberInfo]: 

448 """ 

449 Gets field associated with the choice case. Can be empty. 

450 

451 :returns: Field MemberInfo. 

452 """ 

453 

454 return self._field 

455 

456 

457class ItemInfo: 

458 """ 

459 Item info class which provides information about items of generated enumerable types. 

460 """ 

461 

462 def __init__( 

463 self, 

464 schema_name: str, 

465 py_item: typing.Any, 

466 is_deprecated: bool, 

467 is_removed: bool, 

468 ): 

469 """ 

470 Constructor. 

471 

472 :param schema_name: Name of the item as is defined in Zserio schema. 

473 :param py_item: Reference to the generated item. 

474 """ 

475 

476 self._schema_name = schema_name 

477 self._py_item = py_item 

478 self._is_deprecated = is_deprecated 

479 self._is_removed = is_removed 

480 

481 @property 

482 def schema_name(self) -> str: 

483 """ 

484 Gets name of the item as is defined in Zserio schema. 

485 

486 :returns: Item name in Zserio schema. 

487 """ 

488 

489 return self._schema_name 

490 

491 @property 

492 def py_item(self) -> typing.Any: 

493 """ 

494 Gets reference to the item generated in Python. 

495 

496 :returns: Python item. 

497 """ 

498 

499 return self._py_item 

500 

501 @property 

502 def is_deprecated(self) -> bool: 

503 """ 

504 Gets flag whether the item is deprecated. 

505 

506 :returns: True when the item is deprecated, false otherwise. 

507 """ 

508 

509 return self._is_deprecated 

510 

511 @property 

512 def is_removed(self) -> bool: 

513 """ 

514 Gets flag whether the item is removed. 

515 

516 :returns: True when the item is removed, false otherwise. 

517 """ 

518 

519 return self._is_removed