Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyasn1/type/namedtype.py: 71%

232 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:45 +0000

1# 

2# This file is part of pyasn1 software. 

3# 

4# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> 

5# License: https://pyasn1.readthedocs.io/en/latest/license.html 

6# 

7import sys 

8 

9from pyasn1 import error 

10from pyasn1.type import tag 

11from pyasn1.type import tagmap 

12 

13__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType', 

14 'NamedTypes'] 

15 

16try: 

17 any 

18 

19except NameError: 

20 any = lambda x: bool(filter(bool, x)) 

21 

22 

23class NamedType(object): 

24 """Create named field object for a constructed ASN.1 type. 

25 

26 The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type. 

27 

28 |NamedType| objects are immutable and duck-type Python :class:`tuple` objects 

29 holding *name* and *asn1Object* components. 

30 

31 Parameters 

32 ---------- 

33 name: :py:class:`str` 

34 Field name 

35 

36 asn1Object: 

37 ASN.1 type object 

38 """ 

39 isOptional = False 

40 isDefaulted = False 

41 

42 def __init__(self, name, asn1Object, openType=None): 

43 self.__name = name 

44 self.__type = asn1Object 

45 self.__nameAndType = name, asn1Object 

46 self.__openType = openType 

47 

48 def __repr__(self): 

49 representation = '%s=%r' % (self.name, self.asn1Object) 

50 

51 if self.openType: 

52 representation += ', open type %r' % self.openType 

53 

54 return '<%s object, type %s>' % ( 

55 self.__class__.__name__, representation) 

56 

57 def __eq__(self, other): 

58 return self.__nameAndType == other 

59 

60 def __ne__(self, other): 

61 return self.__nameAndType != other 

62 

63 def __lt__(self, other): 

64 return self.__nameAndType < other 

65 

66 def __le__(self, other): 

67 return self.__nameAndType <= other 

68 

69 def __gt__(self, other): 

70 return self.__nameAndType > other 

71 

72 def __ge__(self, other): 

73 return self.__nameAndType >= other 

74 

75 def __hash__(self): 

76 return hash(self.__nameAndType) 

77 

78 def __getitem__(self, idx): 

79 return self.__nameAndType[idx] 

80 

81 def __iter__(self): 

82 return iter(self.__nameAndType) 

83 

84 @property 

85 def name(self): 

86 return self.__name 

87 

88 @property 

89 def asn1Object(self): 

90 return self.__type 

91 

92 @property 

93 def openType(self): 

94 return self.__openType 

95 

96 # Backward compatibility 

97 

98 def getName(self): 

99 return self.name 

100 

101 def getType(self): 

102 return self.asn1Object 

103 

104 

105class OptionalNamedType(NamedType): 

106 __doc__ = NamedType.__doc__ 

107 

108 isOptional = True 

109 

110 

111class DefaultedNamedType(NamedType): 

112 __doc__ = NamedType.__doc__ 

113 

114 isDefaulted = True 

115 

116 

117class NamedTypes(object): 

118 """Create a collection of named fields for a constructed ASN.1 type. 

119 

120 The NamedTypes object represents a collection of named fields of a constructed ASN.1 type. 

121 

122 *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects 

123 holding *name* as keys and ASN.1 type object as values. 

124 

125 Parameters 

126 ---------- 

127 *namedTypes: :class:`~pyasn1.type.namedtype.NamedType` 

128 

129 Examples 

130 -------- 

131 

132 .. code-block:: python 

133 

134 class Description(Sequence): 

135 ''' 

136 ASN.1 specification: 

137 

138 Description ::= SEQUENCE { 

139 surname IA5String, 

140 first-name IA5String OPTIONAL, 

141 age INTEGER DEFAULT 40 

142 } 

143 ''' 

144 componentType = NamedTypes( 

145 NamedType('surname', IA5String()), 

146 OptionalNamedType('first-name', IA5String()), 

147 DefaultedNamedType('age', Integer(40)) 

148 ) 

149 

150 descr = Description() 

151 descr['surname'] = 'Smith' 

152 descr['first-name'] = 'John' 

153 """ 

154 def __init__(self, *namedTypes, **kwargs): 

155 self.__namedTypes = namedTypes 

156 self.__namedTypesLen = len(self.__namedTypes) 

157 self.__minTagSet = self.__computeMinTagSet() 

158 self.__nameToPosMap = self.__computeNameToPosMap() 

159 self.__tagToPosMap = self.__computeTagToPosMap() 

160 self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {} 

161 self.__uniqueTagMap = self.__computeTagMaps(unique=True) 

162 self.__nonUniqueTagMap = self.__computeTagMaps(unique=False) 

163 self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes 

164 if namedType.isDefaulted or namedType.isOptional]) 

165 self.__hasOpenTypes = any([True for namedType in self.__namedTypes 

166 if namedType.openType]) 

167 

168 self.__requiredComponents = frozenset( 

169 [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted] 

170 ) 

171 self.__keys = frozenset([namedType.name for namedType in self.__namedTypes]) 

172 self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes]) 

173 self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes]) 

174 

175 def __repr__(self): 

176 representation = ', '.join(['%r' % x for x in self.__namedTypes]) 

177 return '<%s object, types %s>' % ( 

178 self.__class__.__name__, representation) 

179 

180 def __eq__(self, other): 

181 return self.__namedTypes == other 

182 

183 def __ne__(self, other): 

184 return self.__namedTypes != other 

185 

186 def __lt__(self, other): 

187 return self.__namedTypes < other 

188 

189 def __le__(self, other): 

190 return self.__namedTypes <= other 

191 

192 def __gt__(self, other): 

193 return self.__namedTypes > other 

194 

195 def __ge__(self, other): 

196 return self.__namedTypes >= other 

197 

198 def __hash__(self): 

199 return hash(self.__namedTypes) 

200 

201 def __getitem__(self, idx): 

202 try: 

203 return self.__namedTypes[idx] 

204 

205 except TypeError: 

206 return self.__namedTypes[self.__nameToPosMap[idx]] 

207 

208 def __contains__(self, key): 

209 return key in self.__nameToPosMap 

210 

211 def __iter__(self): 

212 return (x[0] for x in self.__namedTypes) 

213 

214 if sys.version_info[0] <= 2: 

215 def __nonzero__(self): 

216 return self.__namedTypesLen > 0 

217 else: 

218 def __bool__(self): 

219 return self.__namedTypesLen > 0 

220 

221 def __len__(self): 

222 return self.__namedTypesLen 

223 

224 # Python dict protocol 

225 

226 def values(self): 

227 return self.__values 

228 

229 def keys(self): 

230 return self.__keys 

231 

232 def items(self): 

233 return self.__items 

234 

235 def clone(self): 

236 return self.__class__(*self.__namedTypes) 

237 

238 class PostponedError(object): 

239 def __init__(self, errorMsg): 

240 self.__errorMsg = errorMsg 

241 

242 def __getitem__(self, item): 

243 raise error.PyAsn1Error(self.__errorMsg) 

244 

245 def __computeTagToPosMap(self): 

246 tagToPosMap = {} 

247 for idx, namedType in enumerate(self.__namedTypes): 

248 tagMap = namedType.asn1Object.tagMap 

249 if isinstance(tagMap, NamedTypes.PostponedError): 

250 return tagMap 

251 if not tagMap: 

252 continue 

253 for _tagSet in tagMap.presentTypes: 

254 if _tagSet in tagToPosMap: 

255 return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType)) 

256 tagToPosMap[_tagSet] = idx 

257 

258 return tagToPosMap 

259 

260 def __computeNameToPosMap(self): 

261 nameToPosMap = {} 

262 for idx, namedType in enumerate(self.__namedTypes): 

263 if namedType.name in nameToPosMap: 

264 return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType)) 

265 nameToPosMap[namedType.name] = idx 

266 

267 return nameToPosMap 

268 

269 def __computeAmbiguousTypes(self): 

270 ambiguousTypes = {} 

271 partialAmbiguousTypes = () 

272 for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))): 

273 if namedType.isOptional or namedType.isDefaulted: 

274 partialAmbiguousTypes = (namedType,) + partialAmbiguousTypes 

275 else: 

276 partialAmbiguousTypes = (namedType,) 

277 if len(partialAmbiguousTypes) == len(self.__namedTypes): 

278 ambiguousTypes[idx] = self 

279 else: 

280 ambiguousTypes[idx] = NamedTypes(*partialAmbiguousTypes, **dict(terminal=True)) 

281 return ambiguousTypes 

282 

283 def getTypeByPosition(self, idx): 

284 """Return ASN.1 type object by its position in fields set. 

285 

286 Parameters 

287 ---------- 

288 idx: :py:class:`int` 

289 Field index 

290 

291 Returns 

292 ------- 

293 : 

294 ASN.1 type 

295 

296 Raises 

297 ------ 

298 ~pyasn1.error.PyAsn1Error 

299 If given position is out of fields range 

300 """ 

301 try: 

302 return self.__namedTypes[idx].asn1Object 

303 

304 except IndexError: 

305 raise error.PyAsn1Error('Type position out of range') 

306 

307 def getPositionByType(self, tagSet): 

308 """Return field position by its ASN.1 type. 

309 

310 Parameters 

311 ---------- 

312 tagSet: :class:`~pysnmp.type.tag.TagSet` 

313 ASN.1 tag set distinguishing one ASN.1 type from others. 

314 

315 Returns 

316 ------- 

317 : :py:class:`int` 

318 ASN.1 type position in fields set 

319 

320 Raises 

321 ------ 

322 ~pyasn1.error.PyAsn1Error 

323 If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes* 

324 """ 

325 try: 

326 return self.__tagToPosMap[tagSet] 

327 

328 except KeyError: 

329 raise error.PyAsn1Error('Type %s not found' % (tagSet,)) 

330 

331 def getNameByPosition(self, idx): 

332 """Return field name by its position in fields set. 

333 

334 Parameters 

335 ---------- 

336 idx: :py:class:`idx` 

337 Field index 

338 

339 Returns 

340 ------- 

341 : :py:class:`str` 

342 Field name 

343 

344 Raises 

345 ------ 

346 ~pyasn1.error.PyAsn1Error 

347 If given field name is not present in callee *NamedTypes* 

348 """ 

349 try: 

350 return self.__namedTypes[idx].name 

351 

352 except IndexError: 

353 raise error.PyAsn1Error('Type position out of range') 

354 

355 def getPositionByName(self, name): 

356 """Return field position by filed name. 

357 

358 Parameters 

359 ---------- 

360 name: :py:class:`str` 

361 Field name 

362 

363 Returns 

364 ------- 

365 : :py:class:`int` 

366 Field position in fields set 

367 

368 Raises 

369 ------ 

370 ~pyasn1.error.PyAsn1Error 

371 If *name* is not present or not unique within callee *NamedTypes* 

372 """ 

373 try: 

374 return self.__nameToPosMap[name] 

375 

376 except KeyError: 

377 raise error.PyAsn1Error('Name %s not found' % (name,)) 

378 

379 def getTagMapNearPosition(self, idx): 

380 """Return ASN.1 types that are allowed at or past given field position. 

381 

382 Some ASN.1 serialisation allow for skipping optional and defaulted fields. 

383 Some constructed ASN.1 types allow reordering of the fields. When recovering 

384 such objects it may be important to know which types can possibly be 

385 present at any given position in the field sets. 

386 

387 Parameters 

388 ---------- 

389 idx: :py:class:`int` 

390 Field index 

391 

392 Returns 

393 ------- 

394 : :class:`~pyasn1.type.tagmap.TagMap` 

395 Map if ASN.1 types allowed at given field position 

396 

397 Raises 

398 ------ 

399 ~pyasn1.error.PyAsn1Error 

400 If given position is out of fields range 

401 """ 

402 try: 

403 return self.__ambiguousTypes[idx].tagMap 

404 

405 except KeyError: 

406 raise error.PyAsn1Error('Type position out of range') 

407 

408 def getPositionNearType(self, tagSet, idx): 

409 """Return the closest field position where given ASN.1 type is allowed. 

410 

411 Some ASN.1 serialisation allow for skipping optional and defaulted fields. 

412 Some constructed ASN.1 types allow reordering of the fields. When recovering 

413 such objects it may be important to know at which field position, in field set, 

414 given *tagSet* is allowed at or past *idx* position. 

415 

416 Parameters 

417 ---------- 

418 tagSet: :class:`~pyasn1.type.tag.TagSet` 

419 ASN.1 type which field position to look up 

420 

421 idx: :py:class:`int` 

422 Field position at or past which to perform ASN.1 type look up 

423 

424 Returns 

425 ------- 

426 : :py:class:`int` 

427 Field position in fields set 

428 

429 Raises 

430 ------ 

431 ~pyasn1.error.PyAsn1Error 

432 If *tagSet* is not present or not unique within callee *NamedTypes* 

433 or *idx* is out of fields range 

434 """ 

435 try: 

436 return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet) 

437 

438 except KeyError: 

439 raise error.PyAsn1Error('Type position out of range') 

440 

441 def __computeMinTagSet(self): 

442 minTagSet = None 

443 for namedType in self.__namedTypes: 

444 asn1Object = namedType.asn1Object 

445 

446 try: 

447 tagSet = asn1Object.minTagSet 

448 

449 except AttributeError: 

450 tagSet = asn1Object.tagSet 

451 

452 if minTagSet is None or tagSet < minTagSet: 

453 minTagSet = tagSet 

454 

455 return minTagSet or tag.TagSet() 

456 

457 @property 

458 def minTagSet(self): 

459 """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*. 

460 

461 Some ASN.1 types/serialisation protocols require ASN.1 types to be 

462 arranged based on their numerical tag value. The *minTagSet* property 

463 returns that. 

464 

465 Returns 

466 ------- 

467 : :class:`~pyasn1.type.tagset.TagSet` 

468 Minimal TagSet among ASN.1 types in callee *NamedTypes* 

469 """ 

470 return self.__minTagSet 

471 

472 def __computeTagMaps(self, unique): 

473 presentTypes = {} 

474 skipTypes = {} 

475 defaultType = None 

476 for namedType in self.__namedTypes: 

477 tagMap = namedType.asn1Object.tagMap 

478 if isinstance(tagMap, NamedTypes.PostponedError): 

479 return tagMap 

480 for tagSet in tagMap: 

481 if unique and tagSet in presentTypes: 

482 return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self)) 

483 presentTypes[tagSet] = namedType.asn1Object 

484 skipTypes.update(tagMap.skipTypes) 

485 

486 if defaultType is None: 

487 defaultType = tagMap.defaultType 

488 elif tagMap.defaultType is not None: 

489 return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,)) 

490 

491 return tagmap.TagMap(presentTypes, skipTypes, defaultType) 

492 

493 @property 

494 def tagMap(self): 

495 """Return a *TagMap* object from tags and types recursively. 

496 

497 Return a :class:`~pyasn1.type.tagmap.TagMap` object by 

498 combining tags from *TagMap* objects of children types and 

499 associating them with their immediate child type. 

500 

501 Example 

502 ------- 

503 .. code-block:: python 

504 

505 OuterType ::= CHOICE { 

506 innerType INTEGER 

507 } 

508 

509 Calling *.tagMap* on *OuterType* will yield a map like this: 

510 

511 .. code-block:: python 

512 

513 Integer.tagSet -> Choice 

514 """ 

515 return self.__nonUniqueTagMap 

516 

517 @property 

518 def tagMapUnique(self): 

519 """Return a *TagMap* object from unique tags and types recursively. 

520 

521 Return a :class:`~pyasn1.type.tagmap.TagMap` object by 

522 combining tags from *TagMap* objects of children types and 

523 associating them with their immediate child type. 

524 

525 Example 

526 ------- 

527 .. code-block:: python 

528 

529 OuterType ::= CHOICE { 

530 innerType INTEGER 

531 } 

532 

533 Calling *.tagMapUnique* on *OuterType* will yield a map like this: 

534 

535 .. code-block:: python 

536 

537 Integer.tagSet -> Choice 

538 

539 Note 

540 ---- 

541 

542 Duplicate *TagSet* objects found in the tree of children 

543 types would cause error. 

544 """ 

545 return self.__uniqueTagMap 

546 

547 @property 

548 def hasOptionalOrDefault(self): 

549 return self.__hasOptionalOrDefault 

550 

551 @property 

552 def hasOpenTypes(self): 

553 return self.__hasOpenTypes 

554 

555 @property 

556 def namedTypes(self): 

557 return tuple(self.__namedTypes) 

558 

559 @property 

560 def requiredComponents(self): 

561 return self.__requiredComponents