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
« 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
9from pyasn1 import error
10from pyasn1.type import tag
11from pyasn1.type import tagmap
13__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType',
14 'NamedTypes']
16try:
17 any
19except NameError:
20 any = lambda x: bool(filter(bool, x))
23class NamedType(object):
24 """Create named field object for a constructed ASN.1 type.
26 The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
28 |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
29 holding *name* and *asn1Object* components.
31 Parameters
32 ----------
33 name: :py:class:`str`
34 Field name
36 asn1Object:
37 ASN.1 type object
38 """
39 isOptional = False
40 isDefaulted = False
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
48 def __repr__(self):
49 representation = '%s=%r' % (self.name, self.asn1Object)
51 if self.openType:
52 representation += ', open type %r' % self.openType
54 return '<%s object, type %s>' % (
55 self.__class__.__name__, representation)
57 def __eq__(self, other):
58 return self.__nameAndType == other
60 def __ne__(self, other):
61 return self.__nameAndType != other
63 def __lt__(self, other):
64 return self.__nameAndType < other
66 def __le__(self, other):
67 return self.__nameAndType <= other
69 def __gt__(self, other):
70 return self.__nameAndType > other
72 def __ge__(self, other):
73 return self.__nameAndType >= other
75 def __hash__(self):
76 return hash(self.__nameAndType)
78 def __getitem__(self, idx):
79 return self.__nameAndType[idx]
81 def __iter__(self):
82 return iter(self.__nameAndType)
84 @property
85 def name(self):
86 return self.__name
88 @property
89 def asn1Object(self):
90 return self.__type
92 @property
93 def openType(self):
94 return self.__openType
96 # Backward compatibility
98 def getName(self):
99 return self.name
101 def getType(self):
102 return self.asn1Object
105class OptionalNamedType(NamedType):
106 __doc__ = NamedType.__doc__
108 isOptional = True
111class DefaultedNamedType(NamedType):
112 __doc__ = NamedType.__doc__
114 isDefaulted = True
117class NamedTypes(object):
118 """Create a collection of named fields for a constructed ASN.1 type.
120 The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
122 *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
123 holding *name* as keys and ASN.1 type object as values.
125 Parameters
126 ----------
127 *namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
129 Examples
130 --------
132 .. code-block:: python
134 class Description(Sequence):
135 '''
136 ASN.1 specification:
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 )
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])
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])
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)
180 def __eq__(self, other):
181 return self.__namedTypes == other
183 def __ne__(self, other):
184 return self.__namedTypes != other
186 def __lt__(self, other):
187 return self.__namedTypes < other
189 def __le__(self, other):
190 return self.__namedTypes <= other
192 def __gt__(self, other):
193 return self.__namedTypes > other
195 def __ge__(self, other):
196 return self.__namedTypes >= other
198 def __hash__(self):
199 return hash(self.__namedTypes)
201 def __getitem__(self, idx):
202 try:
203 return self.__namedTypes[idx]
205 except TypeError:
206 return self.__namedTypes[self.__nameToPosMap[idx]]
208 def __contains__(self, key):
209 return key in self.__nameToPosMap
211 def __iter__(self):
212 return (x[0] for x in self.__namedTypes)
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
221 def __len__(self):
222 return self.__namedTypesLen
224 # Python dict protocol
226 def values(self):
227 return self.__values
229 def keys(self):
230 return self.__keys
232 def items(self):
233 return self.__items
235 def clone(self):
236 return self.__class__(*self.__namedTypes)
238 class PostponedError(object):
239 def __init__(self, errorMsg):
240 self.__errorMsg = errorMsg
242 def __getitem__(self, item):
243 raise error.PyAsn1Error(self.__errorMsg)
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
258 return tagToPosMap
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
267 return nameToPosMap
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
283 def getTypeByPosition(self, idx):
284 """Return ASN.1 type object by its position in fields set.
286 Parameters
287 ----------
288 idx: :py:class:`int`
289 Field index
291 Returns
292 -------
293 :
294 ASN.1 type
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
304 except IndexError:
305 raise error.PyAsn1Error('Type position out of range')
307 def getPositionByType(self, tagSet):
308 """Return field position by its ASN.1 type.
310 Parameters
311 ----------
312 tagSet: :class:`~pysnmp.type.tag.TagSet`
313 ASN.1 tag set distinguishing one ASN.1 type from others.
315 Returns
316 -------
317 : :py:class:`int`
318 ASN.1 type position in fields set
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]
328 except KeyError:
329 raise error.PyAsn1Error('Type %s not found' % (tagSet,))
331 def getNameByPosition(self, idx):
332 """Return field name by its position in fields set.
334 Parameters
335 ----------
336 idx: :py:class:`idx`
337 Field index
339 Returns
340 -------
341 : :py:class:`str`
342 Field name
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
352 except IndexError:
353 raise error.PyAsn1Error('Type position out of range')
355 def getPositionByName(self, name):
356 """Return field position by filed name.
358 Parameters
359 ----------
360 name: :py:class:`str`
361 Field name
363 Returns
364 -------
365 : :py:class:`int`
366 Field position in fields set
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]
376 except KeyError:
377 raise error.PyAsn1Error('Name %s not found' % (name,))
379 def getTagMapNearPosition(self, idx):
380 """Return ASN.1 types that are allowed at or past given field position.
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.
387 Parameters
388 ----------
389 idx: :py:class:`int`
390 Field index
392 Returns
393 -------
394 : :class:`~pyasn1.type.tagmap.TagMap`
395 Map if ASN.1 types allowed at given field position
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
405 except KeyError:
406 raise error.PyAsn1Error('Type position out of range')
408 def getPositionNearType(self, tagSet, idx):
409 """Return the closest field position where given ASN.1 type is allowed.
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.
416 Parameters
417 ----------
418 tagSet: :class:`~pyasn1.type.tag.TagSet`
419 ASN.1 type which field position to look up
421 idx: :py:class:`int`
422 Field position at or past which to perform ASN.1 type look up
424 Returns
425 -------
426 : :py:class:`int`
427 Field position in fields set
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)
438 except KeyError:
439 raise error.PyAsn1Error('Type position out of range')
441 def __computeMinTagSet(self):
442 minTagSet = None
443 for namedType in self.__namedTypes:
444 asn1Object = namedType.asn1Object
446 try:
447 tagSet = asn1Object.minTagSet
449 except AttributeError:
450 tagSet = asn1Object.tagSet
452 if minTagSet is None or tagSet < minTagSet:
453 minTagSet = tagSet
455 return minTagSet or tag.TagSet()
457 @property
458 def minTagSet(self):
459 """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
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.
465 Returns
466 -------
467 : :class:`~pyasn1.type.tagset.TagSet`
468 Minimal TagSet among ASN.1 types in callee *NamedTypes*
469 """
470 return self.__minTagSet
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)
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,))
491 return tagmap.TagMap(presentTypes, skipTypes, defaultType)
493 @property
494 def tagMap(self):
495 """Return a *TagMap* object from tags and types recursively.
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.
501 Example
502 -------
503 .. code-block:: python
505 OuterType ::= CHOICE {
506 innerType INTEGER
507 }
509 Calling *.tagMap* on *OuterType* will yield a map like this:
511 .. code-block:: python
513 Integer.tagSet -> Choice
514 """
515 return self.__nonUniqueTagMap
517 @property
518 def tagMapUnique(self):
519 """Return a *TagMap* object from unique tags and types recursively.
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.
525 Example
526 -------
527 .. code-block:: python
529 OuterType ::= CHOICE {
530 innerType INTEGER
531 }
533 Calling *.tagMapUnique* on *OuterType* will yield a map like this:
535 .. code-block:: python
537 Integer.tagSet -> Choice
539 Note
540 ----
542 Duplicate *TagSet* objects found in the tree of children
543 types would cause error.
544 """
545 return self.__uniqueTagMap
547 @property
548 def hasOptionalOrDefault(self):
549 return self.__hasOptionalOrDefault
551 @property
552 def hasOpenTypes(self):
553 return self.__hasOpenTypes
555 @property
556 def namedTypes(self):
557 return tuple(self.__namedTypes)
559 @property
560 def requiredComponents(self):
561 return self.__requiredComponents