123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- #
- # This file is part of pyasn1 software.
- #
- # Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
- # License: http://snmplabs.com/pyasn1/license.html
- #
- import sys
-
- from pyasn1 import error
- from pyasn1.type import tag
- from pyasn1.type import tagmap
-
- __all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType',
- 'NamedTypes']
-
- try:
- any
-
- except NameError:
- any = lambda x: bool(filter(bool, x))
-
-
- class NamedType(object):
- """Create named field object for a constructed ASN.1 type.
-
- The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
-
- |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
- holding *name* and *asn1Object* components.
-
- Parameters
- ----------
- name: :py:class:`str`
- Field name
-
- asn1Object:
- ASN.1 type object
- """
- isOptional = False
- isDefaulted = False
-
- def __init__(self, name, asn1Object, openType=None):
- self.__name = name
- self.__type = asn1Object
- self.__nameAndType = name, asn1Object
- self.__openType = openType
-
- def __repr__(self):
- representation = '%s=%r' % (self.name, self.asn1Object)
-
- if self.openType:
- representation += ' openType: %r' % self.openType
-
- return '<%s object at 0x%x type %s>' % (self.__class__.__name__, id(self), representation)
-
- def __eq__(self, other):
- return self.__nameAndType == other
-
- def __ne__(self, other):
- return self.__nameAndType != other
-
- def __lt__(self, other):
- return self.__nameAndType < other
-
- def __le__(self, other):
- return self.__nameAndType <= other
-
- def __gt__(self, other):
- return self.__nameAndType > other
-
- def __ge__(self, other):
- return self.__nameAndType >= other
-
- def __hash__(self):
- return hash(self.__nameAndType)
-
- def __getitem__(self, idx):
- return self.__nameAndType[idx]
-
- def __iter__(self):
- return iter(self.__nameAndType)
-
- @property
- def name(self):
- return self.__name
-
- @property
- def asn1Object(self):
- return self.__type
-
- @property
- def openType(self):
- return self.__openType
-
- # Backward compatibility
-
- def getName(self):
- return self.name
-
- def getType(self):
- return self.asn1Object
-
-
- class OptionalNamedType(NamedType):
- __doc__ = NamedType.__doc__
-
- isOptional = True
-
-
- class DefaultedNamedType(NamedType):
- __doc__ = NamedType.__doc__
-
- isDefaulted = True
-
-
- class NamedTypes(object):
- """Create a collection of named fields for a constructed ASN.1 type.
-
- The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
-
- *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
- holding *name* as keys and ASN.1 type object as values.
-
- Parameters
- ----------
- *namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
-
- Examples
- --------
-
- .. code-block:: python
-
- class Description(Sequence):
- '''
- ASN.1 specification:
-
- Description ::= SEQUENCE {
- surname IA5String,
- first-name IA5String OPTIONAL,
- age INTEGER DEFAULT 40
- }
- '''
- componentType = NamedTypes(
- NamedType('surname', IA5String()),
- OptionalNamedType('first-name', IA5String()),
- DefaultedNamedType('age', Integer(40))
- )
-
- descr = Description()
- descr['surname'] = 'Smith'
- descr['first-name'] = 'John'
- """
- def __init__(self, *namedTypes, **kwargs):
- self.__namedTypes = namedTypes
- self.__namedTypesLen = len(self.__namedTypes)
- self.__minTagSet = self.__computeMinTagSet()
- self.__nameToPosMap = self.__computeNameToPosMap()
- self.__tagToPosMap = self.__computeTagToPosMap()
- self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
- self.__uniqueTagMap = self.__computeTagMaps(unique=True)
- self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
- self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes
- if namedType.isDefaulted or namedType.isOptional])
- self.__hasOpenTypes = any([True for namedType in self.__namedTypes
- if namedType.openType])
-
- self.__requiredComponents = frozenset(
- [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
- )
- self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
- self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
- self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
-
- def __repr__(self):
- representation = ', '.join(['%r' % x for x in self.__namedTypes])
- return '<%s object at 0x%x types %s>' % (self.__class__.__name__, id(self), representation)
-
- def __eq__(self, other):
- return self.__namedTypes == other
-
- def __ne__(self, other):
- return self.__namedTypes != other
-
- def __lt__(self, other):
- return self.__namedTypes < other
-
- def __le__(self, other):
- return self.__namedTypes <= other
-
- def __gt__(self, other):
- return self.__namedTypes > other
-
- def __ge__(self, other):
- return self.__namedTypes >= other
-
- def __hash__(self):
- return hash(self.__namedTypes)
-
- def __getitem__(self, idx):
- try:
- return self.__namedTypes[idx]
-
- except TypeError:
- return self.__namedTypes[self.__nameToPosMap[idx]]
-
- def __contains__(self, key):
- return key in self.__nameToPosMap
-
- def __iter__(self):
- return (x[0] for x in self.__namedTypes)
-
- if sys.version_info[0] <= 2:
- def __nonzero__(self):
- return self.__namedTypesLen > 0
- else:
- def __bool__(self):
- return self.__namedTypesLen > 0
-
- def __len__(self):
- return self.__namedTypesLen
-
- # Python dict protocol
-
- def values(self):
- return self.__values
-
- def keys(self):
- return self.__keys
-
- def items(self):
- return self.__items
-
- def clone(self):
- return self.__class__(*self.__namedTypes)
-
- class PostponedError(object):
- def __init__(self, errorMsg):
- self.__errorMsg = errorMsg
-
- def __getitem__(self, item):
- raise error.PyAsn1Error(self.__errorMsg)
-
- def __computeTagToPosMap(self):
- tagToPosMap = {}
- for idx, namedType in enumerate(self.__namedTypes):
- tagMap = namedType.asn1Object.tagMap
- if isinstance(tagMap, NamedTypes.PostponedError):
- return tagMap
- if not tagMap:
- continue
- for _tagSet in tagMap.presentTypes:
- if _tagSet in tagToPosMap:
- return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
- tagToPosMap[_tagSet] = idx
-
- return tagToPosMap
-
- def __computeNameToPosMap(self):
- nameToPosMap = {}
- for idx, namedType in enumerate(self.__namedTypes):
- if namedType.name in nameToPosMap:
- return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
- nameToPosMap[namedType.name] = idx
-
- return nameToPosMap
-
- def __computeAmbiguousTypes(self):
- ambigiousTypes = {}
- partialAmbigiousTypes = ()
- for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
- if namedType.isOptional or namedType.isDefaulted:
- partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
- else:
- partialAmbigiousTypes = (namedType,)
- if len(partialAmbigiousTypes) == len(self.__namedTypes):
- ambigiousTypes[idx] = self
- else:
- ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes, **dict(terminal=True))
- return ambigiousTypes
-
- def getTypeByPosition(self, idx):
- """Return ASN.1 type object by its position in fields set.
-
- Parameters
- ----------
- idx: :py:class:`int`
- Field index
-
- Returns
- -------
- :
- ASN.1 type
-
- Raises
- ------
- : :class:`~pyasn1.error.PyAsn1Error`
- If given position is out of fields range
- """
- try:
- return self.__namedTypes[idx].asn1Object
-
- except IndexError:
- raise error.PyAsn1Error('Type position out of range')
-
- def getPositionByType(self, tagSet):
- """Return field position by its ASN.1 type.
-
- Parameters
- ----------
- tagSet: :class:`~pysnmp.type.tag.TagSet`
- ASN.1 tag set distinguishing one ASN.1 type from others.
-
- Returns
- -------
- : :py:class:`int`
- ASN.1 type position in fields set
-
- Raises
- ------
- : :class:`~pyasn1.error.PyAsn1Error`
- If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
- """
- try:
- return self.__tagToPosMap[tagSet]
-
- except KeyError:
- raise error.PyAsn1Error('Type %s not found' % (tagSet,))
-
- def getNameByPosition(self, idx):
- """Return field name by its position in fields set.
-
- Parameters
- ----------
- idx: :py:class:`idx`
- Field index
-
- Returns
- -------
- : :py:class:`str`
- Field name
-
- Raises
- ------
- : :class:`~pyasn1.error.PyAsn1Error`
- If given field name is not present in callee *NamedTypes*
- """
- try:
- return self.__namedTypes[idx].name
-
- except IndexError:
- raise error.PyAsn1Error('Type position out of range')
-
- def getPositionByName(self, name):
- """Return field position by filed name.
-
- Parameters
- ----------
- name: :py:class:`str`
- Field name
-
- Returns
- -------
- : :py:class:`int`
- Field position in fields set
-
- Raises
- ------
- : :class:`~pyasn1.error.PyAsn1Error`
- If *name* is not present or not unique within callee *NamedTypes*
- """
- try:
- return self.__nameToPosMap[name]
-
- except KeyError:
- raise error.PyAsn1Error('Name %s not found' % (name,))
-
- def getTagMapNearPosition(self, idx):
- """Return ASN.1 types that are allowed at or past given field position.
-
- Some ASN.1 serialisation allow for skipping optional and defaulted fields.
- Some constructed ASN.1 types allow reordering of the fields. When recovering
- such objects it may be important to know which types can possibly be
- present at any given position in the field sets.
-
- Parameters
- ----------
- idx: :py:class:`int`
- Field index
-
- Returns
- -------
- : :class:`~pyasn1.type.tagmap.TagMap`
- Map if ASN.1 types allowed at given field position
-
- Raises
- ------
- : :class:`~pyasn1.error.PyAsn1Error`
- If given position is out of fields range
- """
- try:
- return self.__ambiguousTypes[idx].tagMap
-
- except KeyError:
- raise error.PyAsn1Error('Type position out of range')
-
- def getPositionNearType(self, tagSet, idx):
- """Return the closest field position where given ASN.1 type is allowed.
-
- Some ASN.1 serialisation allow for skipping optional and defaulted fields.
- Some constructed ASN.1 types allow reordering of the fields. When recovering
- such objects it may be important to know at which field position, in field set,
- given *tagSet* is allowed at or past *idx* position.
-
- Parameters
- ----------
- tagSet: :class:`~pyasn1.type.tag.TagSet`
- ASN.1 type which field position to look up
-
- idx: :py:class:`int`
- Field position at or past which to perform ASN.1 type look up
-
- Returns
- -------
- : :py:class:`int`
- Field position in fields set
-
- Raises
- ------
- : :class:`~pyasn1.error.PyAsn1Error`
- If *tagSet* is not present or not unique within callee *NamedTypes*
- or *idx* is out of fields range
- """
- try:
- return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
-
- except KeyError:
- raise error.PyAsn1Error('Type position out of range')
-
- def __computeMinTagSet(self):
- minTagSet = None
- for namedType in self.__namedTypes:
- asn1Object = namedType.asn1Object
-
- try:
- tagSet = asn1Object.minTagSet
-
- except AttributeError:
- tagSet = asn1Object.tagSet
-
- if minTagSet is None or tagSet < minTagSet:
- minTagSet = tagSet
-
- return minTagSet or tag.TagSet()
-
- @property
- def minTagSet(self):
- """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
-
- Some ASN.1 types/serialisation protocols require ASN.1 types to be
- arranged based on their numerical tag value. The *minTagSet* property
- returns that.
-
- Returns
- -------
- : :class:`~pyasn1.type.tagset.TagSet`
- Minimal TagSet among ASN.1 types in callee *NamedTypes*
- """
- return self.__minTagSet
-
- def __computeTagMaps(self, unique):
- presentTypes = {}
- skipTypes = {}
- defaultType = None
- for namedType in self.__namedTypes:
- tagMap = namedType.asn1Object.tagMap
- if isinstance(tagMap, NamedTypes.PostponedError):
- return tagMap
- for tagSet in tagMap:
- if unique and tagSet in presentTypes:
- return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
- presentTypes[tagSet] = namedType.asn1Object
- skipTypes.update(tagMap.skipTypes)
-
- if defaultType is None:
- defaultType = tagMap.defaultType
- elif tagMap.defaultType is not None:
- return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
-
- return tagmap.TagMap(presentTypes, skipTypes, defaultType)
-
- @property
- def tagMap(self):
- """Return a *TagMap* object from tags and types recursively.
-
- Return a :class:`~pyasn1.type.tagmap.TagMap` object by
- combining tags from *TagMap* objects of children types and
- associating them with their immediate child type.
-
- Example
- -------
- .. code-block:: python
-
- OuterType ::= CHOICE {
- innerType INTEGER
- }
-
- Calling *.tagMap* on *OuterType* will yield a map like this:
-
- .. code-block:: python
-
- Integer.tagSet -> Choice
- """
- return self.__nonUniqueTagMap
-
- @property
- def tagMapUnique(self):
- """Return a *TagMap* object from unique tags and types recursively.
-
- Return a :class:`~pyasn1.type.tagmap.TagMap` object by
- combining tags from *TagMap* objects of children types and
- associating them with their immediate child type.
-
- Example
- -------
- .. code-block:: python
-
- OuterType ::= CHOICE {
- innerType INTEGER
- }
-
- Calling *.tagMapUnique* on *OuterType* will yield a map like this:
-
- .. code-block:: python
-
- Integer.tagSet -> Choice
-
- Note
- ----
-
- Duplicate *TagSet* objects found in the tree of children
- types would cause error.
- """
- return self.__uniqueTagMap
-
- @property
- def hasOptionalOrDefault(self):
- return self.__hasOptionalOrDefault
-
- @property
- def hasOpenTypes(self):
- return self.__hasOpenTypes
-
- @property
- def namedTypes(self):
- return tuple(self.__namedTypes)
-
- @property
- def requiredComponents(self):
- return self.__requiredComponents
|