Coverage for sbe2/xmlparser/attributes.py: 99%

133 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-29 14:12 +0200

1from lxml.etree import Element 

2from .errors import SchemaParsingError 

3from ..schema import Presence, ByteOrder, PrimitiveType, Composite, Type 

4from .ctx import ParsingContext 

5 

6 

7def parse_name(element: Element) -> str: 

8 """ 

9 Parses the 'name' attribute from an XML element. 

10 

11 Args: 

12 element (Element): The XML element to parse. 

13 

14 Returns: 

15 str: The value of the 'name' attribute. 

16 """ 

17 name = element.get("name", "") 

18 if not name: 

19 raise SchemaParsingError(f"Element {element.tag}is missing 'name' attribute") 

20 return name 

21 

22 

23def parse_description(element: Element) -> str: 

24 """ 

25 Parses the 'description' attribute from an XML element. 

26 

27 Args: 

28 element (Element): The XML element to parse. 

29 

30 Returns: 

31 str: The value of the 'description' attribute, or an empty string if not present. 

32 """ 

33 return element.get("description", "").strip() 

34 

35 

36def parse_since_version(element: Element) -> int: 

37 """ 

38 Parses the 'sinceVersion' attribute from an XML element. 

39 

40 Args: 

41 element (Element): The XML element to parse. 

42 

43 Returns: 

44 int: The value of the 'sinceVersion' attribute, or 0 if not present. 

45 """ 

46 # TODO: must not be greater than schema version 

47 return int(element.get("sinceVersion", "0")) 

48 

49 

50def parse_deprecated(element: Element) -> int | None: 

51 """ 

52 Parses the 'deprecated' attribute from an XML element. 

53 

54 Args: 

55 element (Element): The XML element to parse. 

56 

57 Returns: 

58 int | None: The value of the 'deprecated' attribute, or None if not present. 

59 """ 

60 #TODO: must not be greater than schema version 

61 #TODO: must be greater than sinceVersion 

62 deprecated = element.get("deprecated") 

63 try: 

64 return int(deprecated) if deprecated else None 

65 except (ValueError, TypeError) as e: 

66 raise SchemaParsingError( 

67 f"Invalid deprecated value '{deprecated}' in element {element.tag}" 

68 ) from e 

69 

70 

71def parse_offset(element: Element) -> int | None: 

72 """ 

73 Parses the 'offset' attribute from an XML element. 

74 

75 Args: 

76 element (Element): The XML element to parse. 

77 

78 Returns: 

79 int | None: The value of the 'offset' attribute, or None if not present. 

80 """ 

81 offset = element.get("offset") 

82 try: 

83 return int(offset) if offset else None 

84 except (ValueError, TypeError) as e: 

85 raise SchemaParsingError( 

86 f"Invalid offset value '{offset}' in element {element.tag}" 

87 ) from e 

88 

89 

90def parse_presence(element: Element) -> Presence | None: 

91 """ 

92 Parses the 'presence' attribute from an XML element. 

93 

94 Args: 

95 element (Element): The XML element to parse. 

96 

97 Returns: 

98 Presence: The presence value, defaulting to 'required' if not specified. 

99 """ 

100 presence_str = element.get("presence") 

101 if not presence_str: 

102 return None 

103 try: 

104 return Presence(presence_str) 

105 except ValueError as e: 

106 raise SchemaParsingError( 

107 f"Invalid presence value '{presence_str}' in element {element.tag}" 

108 ) from e 

109 

110 

111def parse_id(element: Element) -> int: 

112 """ 

113 Parses the 'id' attribute from an XML element. 

114 

115 Args: 

116 element (Element): The XML element to parse. 

117 

118 Returns: 

119 int: The value of the 'id' attribute 

120 """ 

121 id_str = element.get("id") 

122 try: 

123 return int(id_str) 

124 except (ValueError, TypeError) as e: 

125 raise SchemaParsingError( 

126 f"Invalid id value '{id_str}' in element {element.tag}" 

127 ) from e 

128 

129 

130def parse_semantic_type(element: Element) -> str: 

131 """ 

132 Parses the 'semanticType' attribute from an XML element. 

133 

134 Args: 

135 element (Element): The XML element to parse. 

136 

137 Returns: 

138 str: The value of the 'semanticType' attribute, or an empty string if not present. 

139 """ 

140 return element.get("semanticType", "") 

141 

142 

143def parse_alignment(element: Element) -> int | None: 

144 """ 

145 Parses the 'alignment' attribute from an XML element. 

146 

147 Args: 

148 element (Element): The XML element to parse. 

149 

150 Returns: 

151 int | None: The value of the 'alignment' attribute, or None if not present. 

152 """ 

153 alignment = element.get("alignment") 

154 try: 

155 return int(alignment) if alignment else None 

156 except (ValueError, TypeError) as e: 

157 raise SchemaParsingError( 

158 f"Invalid alignment value '{alignment}' in element {element.tag}" 

159 ) from e 

160 

161 

162def parse_block_length(element: Element) -> int | None: 

163 """ 

164 Parses the 'blockLength' attribute from an XML element. 

165 

166 Args: 

167 element (Element): The XML element to parse. 

168 

169 Returns: 

170 int | None: The value of the 'blockLength' attribute, or None if not present. 

171 """ 

172 block_length = element.get("blockLength") 

173 try: 

174 return int(block_length) if block_length else None 

175 except (ValueError, TypeError) as e: 

176 raise SchemaParsingError( 

177 f"Invalid blockLength value '{block_length}' in element {element.tag}" 

178 ) from e 

179 

180def parse_encoding_type(element: Element) -> Type: 

181 """ 

182 Parses the 'encodingType' attribute from an XML element. 

183 

184 Args: 

185 element (Element): The XML element to parse. 

186 

187 Returns: 

188 Type: The type associated with the 'encodingType' attribute 

189 """ 

190 encoding_type = element.get("encodingType", '') 

191 if not encoding_type: 

192 raise SchemaParsingError( 

193 f"Element {element.tag} is missing 'encodingType' attribute" 

194 ) 

195 return encoding_type 

196 

197 

198def parse_min_value(element: Element) -> int | None: 

199 """ 

200 Parses the 'minValue' attribute from an XML element. 

201 

202 Args: 

203 element (Element): The XML element to parse. 

204 

205 Returns: 

206 int | None: The value of the 'minValue' attribute, or None if not present. 

207 """ 

208 min_value = element.get("minValue") 

209 try: 

210 return int(min_value) if min_value else None 

211 except (ValueError, TypeError) as e: 

212 raise SchemaParsingError( 

213 f"Invalid minValue '{min_value}' in element {element.tag}" 

214 ) from e 

215 

216def parse_max_value(element: Element) -> int | None: 

217 """ 

218 Parses the 'maxValue' attribute from an XML element. 

219 

220 Args: 

221 element (Element): The XML element to parse. 

222 

223 Returns: 

224 int | None: The value of the 'maxValue' attribute, or None if not present. 

225 """ 

226 max_value = element.get("maxValue") 

227 try: 

228 return int(max_value) if max_value else None 

229 except (ValueError, TypeError) as e: 

230 raise SchemaParsingError( 

231 f"Invalid maxValue '{max_value}' in element {element.tag}" 

232 ) from e 

233 

234 

235def parse_null_value(element: Element) -> int | None: 

236 """ 

237 Parses the 'nullValue' attribute from an XML element. 

238 

239 Args: 

240 element (Element): The XML element to parse. 

241 

242 Returns: 

243 int | None: The value of the 'nullValue' attribute, or None if not present. 

244 """ 

245 null_value = element.get("nullValue") 

246 try: 

247 return int(null_value) if null_value else None 

248 except (ValueError, TypeError) as e: 

249 raise SchemaParsingError( 

250 f"Invalid nullValue '{null_value}' in element {element.tag}" 

251 ) from e 

252 

253def parse_character_encoding(element: Element) -> str | None: 

254 """ 

255 Parses the 'characterEncoding' attribute from an XML element. 

256 

257 Args: 

258 element (Element): The XML element to parse. 

259 

260 Returns: 

261 str: The value of the 'characterEncoding' attribute, or None if not present. 

262 """ 

263 character_encoding = element.get("characterEncoding", "") 

264 return character_encoding if character_encoding else None 

265 

266 

267def parse_primitive_type(element: Element) -> PrimitiveType: 

268 """ 

269 Parses the 'primitiveType' attribute from an XML element. 

270 

271 Args: 

272 element (Element): The XML element to parse. 

273 

274 Returns: 

275 str: The value of the 'primitiveType' attribute. 

276 """ 

277 primitive_type = element.get("primitiveType", "") 

278 if not primitive_type: 

279 raise SchemaParsingError( 

280 f"Element {element.tag} is missing 'primitiveType' attribute" 

281 ) 

282 

283 pt = PrimitiveType.by_name.get(primitive_type) 

284 if pt is None: 

285 raise SchemaParsingError( 

286 f"Unknown primitive type '{primitive_type}' in element {element.tag}" 

287 ) 

288 return pt 

289 

290 

291def parse_value_ref(element: Element) -> str | None: 

292 """ 

293 Parses the 'valueRef' attribute from an XML element. 

294 

295 Args: 

296 element (Element): The XML element to parse. 

297 

298 Returns: 

299 str | None: The value of the 'valueRef' attribute, or None if not present. 

300 """ 

301 vr = element.get("valueRef", "") 

302 return vr if vr else None 

303 

304 

305def parse_type(element:Element) -> str: 

306 """ 

307 Parses the 'type' attribute from an XML element. 

308 

309 Args: 

310 element (Element): The XML element to parse. 

311 

312 Returns: 

313 str: The value of the 'type' attribute. 

314 """ 

315 type_value = element.get("type", "") 

316 if not type_value: 

317 raise SchemaParsingError( 

318 f"Element {element.tag} is missing 'type' attribute" 

319 ) 

320 return type_value 

321 

322 

323def parse_length(element: Element) -> int: 

324 """ 

325 Parses the 'length' attribute from an XML element. 

326 

327 Args: 

328 element (Element): The XML element to parse. 

329 

330 Returns: 

331 int: The value of the 'length' attribute. 

332 """ 

333 length_str = element.get("length", "1") 

334 try: 

335 return int(length_str) 

336 except (ValueError, TypeError) as e: 

337 raise SchemaParsingError( 

338 f"Invalid length value '{length_str}' in element {element.tag}" 

339 ) from e 

340 

341def parse_byte_order(element: Element) -> ByteOrder: 

342 """ 

343 Parses the 'byteOrder' attribute from an XML element. 

344 

345 Args: 

346 element (Element): The XML element to parse. 

347 

348 Returns: 

349 ByteOrder: The byte order value, defaulting to 'littleEndian' if not specified. 

350 """ 

351 byte_order_str = element.get("byteOrder", "littleEndian") 

352 try: 

353 return ByteOrder(byte_order_str) 

354 except ValueError as e: 

355 raise SchemaParsingError( 

356 f"Invalid byte order '{byte_order_str}' in element {element.tag}" 

357 ) from e 

358 

359def parse_version(element: Element) -> int: 

360 """ 

361 Parses the 'version' attribute from an XML element. 

362 

363 Args: 

364 element (Element): The XML element to parse. 

365 

366 Returns: 

367 int: The value of the 'version' attribute. 

368 """ 

369 version_str = element.get("version") 

370 try: 

371 return int(version_str) 

372 except (ValueError, TypeError) as e: 

373 raise SchemaParsingError( 

374 f"Invalid version value '{version_str}' in element {element.tag}" 

375 ) from e 

376 

377 

378def parse_header_type(element: Element) -> str: 

379 """ 

380 Parses the 'headerType' attribute from an XML element. 

381 

382 Args: 

383 element (Element): The XML element to parse. 

384 

385 Returns: 

386 str: The value of the 'headerType' attribute. 

387 """ 

388 header_type = element.get("headerType", "messageHeader") 

389 if not header_type: 

390 raise SchemaParsingError( 

391 f"Element {element.tag} is missing 'headerType' attribute" 

392 ) 

393 return header_type 

394 

395def parse_package(element: Element, required: bool = True) -> str | None: 

396 """ 

397 Parses the 'package' attribute from an XML element. 

398 

399 Args: 

400 element (Element): The XML element to parse. 

401 required (bool): If set raises an exception on empty value. 

402 

403 Returns: 

404 str | None: The value of the 'package' attribute. 

405 """ 

406 package = element.get("package", "") 

407 if not package and required: 

408 raise SchemaParsingError( 

409 f"Element {element.tag} is missing 'package' attribute" 

410 ) 

411 return package or None 

412 

413 

414def parse_semantic_version(element: Element) -> str: 

415 """ 

416 Parses the 'semanticVersion' attribute from an XML element. 

417 

418 Args: 

419 element (Element): The XML element to parse. 

420 

421 Returns: 

422 str: The value of the 'semanticVersion' attribute. 

423 """ 

424 return element.get("semanticVersion", "") 

425 # TODO: Validate semantic version format if necessary 

426 # TODO: is empty string valid? 

427 

428 

429def parse_dimension_type(node, ctx: ParsingContext) -> Composite: 

430 """ 

431 Parses the 'dimensionType' attribute from an XML element. 

432 

433 Args: 

434 node (Element): The XML element to parse. 

435 ctx (ParsingContext): The context of parsing. 

436 

437 Returns: 

438 str: The value of the 'dimensionType' attribute. 

439 """ 

440 dimension_type = node.get("dimensionType", "groupSizeEncoding") 

441 if not dimension_type: 

442 raise SchemaParsingError( 

443 f"Element {node.tag} is missing 'dimensionType' attribute" 

444 ) 

445 # TODO: validate that the composite meets requirements for a dimension type 

446 try: 

447 return ctx.types.get_composite(dimension_type) 

448 except Exception as e: 

449 raise SchemaParsingError( 

450 f"Unknown or invalid dimension type '{dimension_type}' in element {node.tag}" 

451 ) from e