diff options
Diffstat (limited to 'pyasn1/type/univ.py')
-rw-r--r-- | pyasn1/type/univ.py | 558 |
1 files changed, 385 insertions, 173 deletions
diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py index 7fab69f..b39c533 100644 --- a/pyasn1/type/univ.py +++ b/pyasn1/type/univ.py @@ -31,15 +31,17 @@ __all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null', # "Simple" ASN.1 types (yet incomplete) -class Integer(base.AbstractSimpleAsn1Item): - """Create |ASN.1| type or object. +class Integer(base.SimpleAsn1Type): + """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python :class:`int` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object - Python integer or string literal or |ASN.1| class instance. + Python :class:`int` or :class:`str` literal or |ASN.1| class + instance. If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -52,7 +54,7 @@ class Integer(base.AbstractSimpleAsn1Item): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -94,13 +96,13 @@ class Integer(base.AbstractSimpleAsn1Item): namedValues = namedval.NamedValues() # Optimization for faster codec lookup - typeId = base.AbstractSimpleAsn1Item.getTypeId() + typeId = base.SimpleAsn1Type.getTypeId() def __init__(self, value=noValue, **kwargs): if 'namedValues' not in kwargs: kwargs['namedValues'] = self.namedValues - base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) + base.SimpleAsn1Type.__init__(self, value, **kwargs) def __and__(self, value): return self.clone(self._value & value) @@ -187,7 +189,7 @@ class Integer(base.AbstractSimpleAsn1Item): def __rdivmod__(self, value): return self.clone(divmod(value, self._value)) - __hash__ = base.AbstractSimpleAsn1Item.__hash__ + __hash__ = base.SimpleAsn1Type.__hash__ def __int__(self): return int(self._value) @@ -276,14 +278,16 @@ class Integer(base.AbstractSimpleAsn1Item): class Boolean(Integer): - """Create |ASN.1| type or object. + """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python :class:`int` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object - Python integer or boolean or string literal or |ASN.1| class instance. + Python :class:`int` or :class:`str` literal or |ASN.1| class + instance. If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -296,7 +300,7 @@ class Boolean(Integer): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -355,17 +359,19 @@ class SizedInteger(SizedIntegerBase): return self.bitLength -class BitString(base.AbstractSimpleAsn1Item): +class BitString(base.SimpleAsn1Type): """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type both Python :class:`tuple` (as a tuple + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type both Python :class:`tuple` (as a tuple of bits) and :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object - Python integer or string literal representing binary or hexadecimal - number or sequence of integer bits or |ASN.1| object. + Python :class:`int` or :class:`str` literal representing binary + or hexadecimal number or sequence of integer bits or |ASN.1| object. + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -386,7 +392,7 @@ class BitString(base.AbstractSimpleAsn1Item): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -432,7 +438,7 @@ class BitString(base.AbstractSimpleAsn1Item): namedValues = namedval.NamedValues() # Optimization for faster codec lookup - typeId = base.AbstractSimpleAsn1Item.getTypeId() + typeId = base.SimpleAsn1Type.getTypeId() defaultBinValue = defaultHexValue = noValue @@ -461,7 +467,7 @@ class BitString(base.AbstractSimpleAsn1Item): if 'namedValues' not in kwargs: kwargs['namedValues'] = self.namedValues - base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) + base.SimpleAsn1Type.__init__(self, value, **kwargs) def __str__(self): return self.asBinary() @@ -720,18 +726,22 @@ except NameError: # Python 2.4 return True -class OctetString(base.AbstractSimpleAsn1Item): +class OctetString(base.SimpleAsn1Type): """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3 :class:`bytes`. - When used in Unicode context, |ASN.1| type assumes "|encoding|" serialisation. + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python 2 :class:`str` or + Python 3 :class:`bytes`. When used in Unicode context, |ASN.1| type + assumes "|encoding|" serialisation. Keyword Args ------------ - value: :class:`str`, :class:`bytes` or |ASN.1| object - string (Python 2) or bytes (Python 3), alternatively unicode object - (Python 2) or string (Python 3) representing character string to be - serialised into octets (note `encoding` parameter) or |ASN.1| object. + value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object + class:`str` (Python 2) or :class:`bytes` (Python 3), alternatively + class:`unicode` object (Python 2) or :class:`str` (Python 3) + representing character string to be serialised into octets + (note `encoding` parameter) or |ASN.1| object. + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -754,7 +764,7 @@ class OctetString(base.AbstractSimpleAsn1Item): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -786,7 +796,7 @@ class OctetString(base.AbstractSimpleAsn1Item): subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup - typeId = base.AbstractSimpleAsn1Item.getTypeId() + typeId = base.SimpleAsn1Type.getTypeId() defaultBinValue = defaultHexValue = noValue encoding = 'iso-8859-1' @@ -816,26 +826,33 @@ class OctetString(base.AbstractSimpleAsn1Item): if 'encoding' not in kwargs: kwargs['encoding'] = self.encoding - base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) + base.SimpleAsn1Type.__init__(self, value, **kwargs) if sys.version_info[0] <= 2: def prettyIn(self, value): if isinstance(value, str): return value + elif isinstance(value, unicode): try: return value.encode(self.encoding) + except (LookupError, UnicodeEncodeError): - raise error.PyAsn1Error( - "Can't encode string '%s' with codec %s" % (value, self.encoding) + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeEncodeError( + "Can't encode string '%s' with codec " + "%s" % (value, self.encoding), exc ) + elif isinstance(value, (tuple, list)): try: return ''.join([chr(x) for x in value]) + except ValueError: raise error.PyAsn1Error( "Bad %s initializer '%s'" % (self.__class__.__name__, value) ) + else: return str(value) @@ -847,8 +864,10 @@ class OctetString(base.AbstractSimpleAsn1Item): return self._value.decode(self.encoding) except UnicodeDecodeError: - raise error.PyAsn1Error( - "Can't decode string '%s' with codec %s" % (self._value, self.encoding) + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeDecodeError( + "Can't decode string '%s' with codec " + "%s" % (self._value, self.encoding), exc ) def asOctets(self): @@ -861,19 +880,26 @@ class OctetString(base.AbstractSimpleAsn1Item): def prettyIn(self, value): if isinstance(value, bytes): return value + elif isinstance(value, str): try: return value.encode(self.encoding) + except UnicodeEncodeError: - raise error.PyAsn1Error( - "Can't encode string '%s' with '%s' codec" % (value, self.encoding) + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeEncodeError( + "Can't encode string '%s' with '%s' " + "codec" % (value, self.encoding), exc ) elif isinstance(value, OctetString): # a shortcut, bytes() would work the same way return value.asOctets() - elif isinstance(value, base.AbstractSimpleAsn1Item): # this mostly targets Integer objects + + elif isinstance(value, base.SimpleAsn1Type): # this mostly targets Integer objects return self.prettyIn(str(value)) + elif isinstance(value, (tuple, list)): return self.prettyIn(bytes(value)) + else: return bytes(value) @@ -882,8 +908,11 @@ class OctetString(base.AbstractSimpleAsn1Item): return self._value.decode(self.encoding) except UnicodeDecodeError: - raise error.PyAsn1Error( - "Can't decode string '%s' with '%s' codec at '%s'" % (self._value, self.encoding, self.__class__.__name__) + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeDecodeError( + "Can't decode string '%s' with '%s' codec at " + "'%s'" % (self._value, self.encoding, + self.__class__.__name__), exc ) def __bytes__(self): @@ -1028,19 +1057,22 @@ class OctetString(base.AbstractSimpleAsn1Item): class Null(OctetString): """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python :class:`str` objects (always empty). + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`str` objects + (always empty). Keyword Args ------------ - value: :class:`str` or :py:class:`~pyasn1.type.univ.Null` object - Python empty string literal or any object that evaluates to `False` + value: :class:`str` or |ASN.1| object + Python empty :class:`str` literal or any object that evaluates to :obj:`False` + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -1081,15 +1113,18 @@ else: numericTypes = intTypes + (float,) -class ObjectIdentifier(base.AbstractSimpleAsn1Item): +class ObjectIdentifier(base.SimpleAsn1Type): """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python :class:`tuple` objects (tuple of non-negative integers). + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`tuple` objects + (tuple of non-negative integers). Keyword Args ------------ value: :class:`tuple`, :class:`str` or |ASN.1| object - Python sequence of :class:`int` or string literal or |ASN.1| object. + Python sequence of :class:`int` or :class:`str` literal or |ASN.1| object. + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -1099,7 +1134,7 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -1131,7 +1166,7 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup - typeId = base.AbstractSimpleAsn1Item.getTypeId() + typeId = base.SimpleAsn1Type.getTypeId() def __add__(self, other): return self.clone(self._value + other) @@ -1173,8 +1208,8 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): Returns ------- : :class:`bool` - :class:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object - or :class:`False` otherwise. + :obj:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object + or :obj:`False` otherwise. """ l = len(self) if l <= len(other): @@ -1214,10 +1249,11 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): return '.'.join([str(x) for x in value]) -class Real(base.AbstractSimpleAsn1Item): +class Real(base.SimpleAsn1Type): """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python :class:`float` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`float` objects. Additionally, |ASN.1| objects behave like a :class:`tuple` in which case its elements are mantissa, base and exponent. @@ -1225,7 +1261,8 @@ class Real(base.AbstractSimpleAsn1Item): ------------ value: :class:`tuple`, :class:`float` or |ASN.1| object Python sequence of :class:`int` (representing mantissa, base and - exponent) or float instance or *Real* class instance. + exponent) or :class:`float` instance or |ASN.1| object. + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -1235,7 +1272,7 @@ class Real(base.AbstractSimpleAsn1Item): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -1278,7 +1315,7 @@ class Real(base.AbstractSimpleAsn1Item): subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup - typeId = base.AbstractSimpleAsn1Item.getTypeId() + typeId = base.SimpleAsn1Type.getTypeId() @staticmethod def __normalizeBase10(value): @@ -1342,8 +1379,8 @@ class Real(base.AbstractSimpleAsn1Item): Returns ------- : :class:`bool` - :class:`True` if calling object represents plus infinity - or :class:`False` otherwise. + :obj:`True` if calling object represents plus infinity + or :obj:`False` otherwise. """ return self._value == self._plusInf @@ -1355,8 +1392,8 @@ class Real(base.AbstractSimpleAsn1Item): Returns ------- : :class:`bool` - :class:`True` if calling object represents minus infinity - or :class:`False` otherwise. + :obj:`True` if calling object represents minus infinity + or :obj:`False` otherwise. """ return self._value == self._minusInf @@ -1479,7 +1516,7 @@ class Real(base.AbstractSimpleAsn1Item): def __bool__(self): return bool(float(self)) - __hash__ = base.AbstractSimpleAsn1Item.__hash__ + __hash__ = base.SimpleAsn1Type.__hash__ def __getitem__(self, idx): if self._value in self._inf: @@ -1500,14 +1537,16 @@ class Real(base.AbstractSimpleAsn1Item): class Enumerated(Integer): - """Create |ASN.1| type or object. + """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python :class:`int` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object - Python integer or string literal or |ASN.1| class instance. + Python :class:`int` or :class:`str` literal or |ASN.1| object. + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -1520,7 +1559,7 @@ class Enumerated(Integer): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples @@ -1566,10 +1605,11 @@ class Enumerated(Integer): # "Structured" ASN.1 types -class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): - """Create |ASN.1| type. +class SequenceOfAndSetOfBase(base.ConstructedAsn1Type): + """Create |ASN.1| schema or value object. - |ASN.1| objects are mutable and duck-type Python :class:`list` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, + its objects are mutable and duck-type Python :class:`list` objects. Keyword Args ------------ @@ -1610,7 +1650,9 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): raise error.PyAsn1Error('Conflicting positional and keyword params!') kwargs['componentType'] = value - base.AbstractConstructedAsn1Item.__init__(self, **kwargs) + self._componentValues = noValue + + base.ConstructedAsn1Type.__init__(self, **kwargs) # Python list protocol @@ -1628,24 +1670,36 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): except error.PyAsn1Error: raise IndexError(sys.exc_info()[1]) - def clear(self): - self._componentValues = [] - def append(self, value): - self[len(self)] = value + if self._componentValues is noValue: + pos = 0 + + else: + pos = len(self._componentValues) + + self[pos] = value def count(self, value): - return self._componentValues.count(value) + return list(self._componentValues.values()).count(value) def extend(self, values): for value in values: self.append(value) + if self._componentValues is noValue: + self._componentValues = {} + def index(self, value, start=0, stop=None): if stop is None: stop = len(self) + + indices, values = zip(*self._componentValues.items()) + + # TODO: remove when Py2.5 support is gone + values = list(values) + try: - return self._componentValues.index(value, start, stop) + return indices[values.index(value, start, stop)] except error.PyAsn1Error: raise ValueError(sys.exc_info()[1]) @@ -1654,15 +1708,24 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): self._componentValues.reverse() def sort(self, key=None, reverse=False): - self._componentValues.sort(key=key, reverse=reverse) + self._componentValues = dict( + enumerate(sorted(self._componentValues.values(), + key=key, reverse=reverse))) + + def __len__(self): + if self._componentValues is noValue or not self._componentValues: + return 0 + + return max(self._componentValues) + 1 def __iter__(self): - return iter(self._componentValues) + for idx in range(0, len(self)): + yield self.getComponentByPosition(idx) def _cloneComponentValues(self, myClone, cloneValueFlag): - for idx, componentValue in enumerate(self._componentValues): + for idx, componentValue in self._componentValues.items(): if componentValue is not noValue: - if isinstance(componentValue, base.AbstractConstructedAsn1Item): + if isinstance(componentValue, base.ConstructedAsn1Type): myClone.setComponentByPosition( idx, componentValue.clone(cloneValueFlag=cloneValueFlag) ) @@ -1689,8 +1752,8 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): object instead of the requested component. instantiate: :class:`bool` - If `True` (default), inner component will be automatically instantiated. - If 'False' either existing component or the `noValue` object will be + If :obj:`True` (default), inner component will be automatically instantiated. + If :obj:`False` either existing component or the :class:`NoValue` object will be returned. Returns @@ -1735,10 +1798,21 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): # returns noValue s.getComponentByPosition(0, instantiate=False) """ + if isinstance(idx, slice): + indices = tuple(range(len(self))) + return [self.getComponentByPosition(subidx, default, instantiate) + for subidx in indices[idx]] + + if idx < 0: + idx = len(self) + idx + if idx < 0: + raise error.PyAsn1Error( + 'SequenceOf/SetOf index is out of range') + try: componentValue = self._componentValues[idx] - except IndexError: + except (KeyError, error.PyAsn1Error): if not instantiate: return default @@ -1773,15 +1847,16 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. verifyConstraints: :class:`bool` - If `False`, skip constraints validation + If :obj:`False`, skip constraints validation matchTags: :class:`bool` - If `False`, skip component tags matching + If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` - If `False`, skip component constraints matching + If :obj:`False`, skip component constraints matching Returns ------- @@ -1789,38 +1864,77 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): Raises ------ - IndexError: + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer + IndexError When idx > len(self) """ + if isinstance(idx, slice): + indices = tuple(range(len(self))) + startIdx = indices and indices[idx][0] or 0 + for subIdx, subValue in enumerate(value): + self.setComponentByPosition( + startIdx + subIdx, subValue, verifyConstraints, + matchTags, matchConstraints) + return self + + if idx < 0: + idx = len(self) + idx + if idx < 0: + raise error.PyAsn1Error( + 'SequenceOf/SetOf index is out of range') + componentType = self.componentType - try: - currentValue = self._componentValues[idx] - except IndexError: - currentValue = noValue + if self._componentValues is noValue: + componentValues = {} - if len(self._componentValues) < idx: - raise error.PyAsn1Error('Component index out of range') + else: + componentValues = self._componentValues + + currentValue = componentValues.get(idx, noValue) if value is noValue: if componentType is not None: value = componentType.clone() + elif currentValue is noValue: raise error.PyAsn1Error('Component type not defined') + elif not isinstance(value, base.Asn1Item): - if componentType is not None and isinstance(componentType, base.AbstractSimpleAsn1Item): + if (componentType is not None and + isinstance(componentType, base.SimpleAsn1Type)): value = componentType.clone(value=value) - elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item): + + elif (currentValue is not noValue and + isinstance(currentValue, base.SimpleAsn1Type)): value = currentValue.clone(value=value) + else: - raise error.PyAsn1Error('Non-ASN.1 value %r and undefined component type at %r' % (value, self)) - elif componentType is not None: - if self.strictConstraints: - if not componentType.isSameTypeWith(value, matchTags, matchConstraints): - raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) + raise error.PyAsn1Error( + 'Non-ASN.1 value %r and undefined component' + ' type at %r' % (value, self)) + + elif componentType is not None and (matchTags or matchConstraints): + subtypeChecker = ( + self.strictConstraints and + componentType.isSameTypeWith or + componentType.isSuperTypeOf) + + if not subtypeChecker(value, matchTags, matchConstraints): + # TODO: we should wrap componentType with UnnamedType to carry + # additional properties associated with componentType + if componentType.typeId != Any.typeId: + raise error.PyAsn1Error( + 'Component value is tag-incompatible: %r vs ' + '%r' % (value, componentType)) + else: - if not componentType.isSuperTypeOf(value, matchTags, matchConstraints): - raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) + if not componentType.isSuperTypeOf( + value, matchTags, matchConstraints): + raise error.PyAsn1Error( + 'Component value is tag-incompatible: ' + '%r vs %r' % (value, componentType)) if verifyConstraints and value.isValue: try: @@ -1830,10 +1944,9 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): exType, exValue, exTb = sys.exc_info() raise exType('%s at %s' % (exValue, self.__class__.__name__)) - if currentValue is noValue: - self._componentValues.append(value) - else: - self._componentValues[idx] = value + componentValues[idx] = value + + self._componentValues = componentValues return self @@ -1842,16 +1955,44 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): if self.componentType is not None: return self.componentType.tagMap + @property + def components(self): + return [self._componentValues[idx] + for idx in sorted(self._componentValues)] + + def clear(self): + """Remove all components and become an empty |ASN.1| value object. + + Has the same effect on |ASN.1| object as it does on :class:`list` + built-in. + """ + self._componentValues = {} + return self + + def reset(self): + """Remove all components and become a |ASN.1| schema object. + + See :meth:`isValue` property for more information on the + distinction between value and schema objects. + """ + self._componentValues = noValue + return self + def prettyPrint(self, scope=0): scope += 1 representation = self.__class__.__name__ + ':\n' - for idx, componentValue in enumerate(self._componentValues): + + if not self.isValue: + return representation + + for idx, componentValue in enumerate(self): representation += ' ' * scope if (componentValue is noValue and self.componentType is not None): representation += '<empty>' else: representation += componentValue.prettyPrint(scope) + return representation def prettyPrintType(self, scope=0): @@ -1867,17 +2008,17 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. - If *isValue* is `False` then this object represents just ASN.1 schema. + If *isValue* is :obj:`False` then this object represents just ASN.1 schema. - If *isValue* is `True` then, in addition to its ASN.1 schema features, - this object can also be used like a Python built-in object (e.g. `int`, - `str`, `dict` etc.). + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, + this object can also be used like a Python built-in object + (e.g. :class:`int`, :class:`str`, :class:`dict` etc.). Returns ------- : :class:`bool` - :class:`False` if object represents just ASN.1 schema. - :class:`True` if object represents ASN.1 schema and can be used as a normal value. + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a normal value. Note ---- @@ -1890,7 +2031,13 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): The PyASN1 value objects can **additionally** participate in many operations involving regular Python objects (e.g. arithmetic, comprehension etc). """ - for componentValue in self._componentValues: + if self._componentValues is noValue: + return False + + if len(self._componentValues) != len(self): + return False + + for componentValue in self._componentValues.values(): if componentValue is noValue or not componentValue.isValue: return False @@ -1951,10 +2098,11 @@ class SetOf(SequenceOfAndSetOfBase): typeId = SequenceOfAndSetOfBase.getTypeId() -class SequenceAndSetBase(base.AbstractConstructedAsn1Item): - """Create |ASN.1| type. +class SequenceAndSetBase(base.ConstructedAsn1Type): + """Create |ASN.1| schema or value object. - |ASN.1| objects are mutable and duck-type Python :class:`dict` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, + its objects are mutable and duck-type Python :class:`dict` objects. Keyword Args ------------ @@ -2042,8 +2190,12 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): def __init__(self, **kwargs): - base.AbstractConstructedAsn1Item.__init__(self, **kwargs) + base.ConstructedAsn1Type.__init__(self, **kwargs) self._componentTypeLen = len(self.componentType) + if self._componentTypeLen: + self._componentValues = [] + else: + self._componentValues = noValue self._dynamicNames = self._componentTypeLen or self.DynamicNames() def __getitem__(self, idx): @@ -2086,6 +2238,9 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): else: return key in self._dynamicNames + def __len__(self): + return len(self._componentValues) + def __iter__(self): return iter(self.componentType or self._dynamicNames) @@ -2112,13 +2267,36 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): self[k] = mappingValue[k] def clear(self): + """Remove all components and become an empty |ASN.1| value object. + + Has the same effect on |ASN.1| object as it does on :class:`dict` + built-in. + """ self._componentValues = [] self._dynamicNames = self.DynamicNames() + return self + + def reset(self): + """Remove all components and become a |ASN.1| schema object. + + See :meth:`isValue` property for more information on the + distinction between value and schema objects. + """ + self._componentValues = noValue + self._dynamicNames = self.DynamicNames() + return self + + @property + def components(self): + return self._componentValues def _cloneComponentValues(self, myClone, cloneValueFlag): + if self._componentValues is noValue: + return + for idx, componentValue in enumerate(self._componentValues): if componentValue is not noValue: - if isinstance(componentValue, base.AbstractConstructedAsn1Item): + if isinstance(componentValue, base.ConstructedAsn1Type): myClone.setComponentByPosition( idx, componentValue.clone(cloneValueFlag=cloneValueFlag) ) @@ -2142,14 +2320,16 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): object instead of the requested component. instantiate: :class:`bool` - If `True` (default), inner component will be automatically instantiated. - If 'False' either existing component or the `noValue` object will be - returned. + If :obj:`True` (default), inner component will be automatically + instantiated. + If :obj:`False` either existing component or the :class:`NoValue` + object will be returned. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` - Instantiate |ASN.1| component type or return existing component value + Instantiate |ASN.1| component type or return existing + component value """ if self._componentTypeLen: idx = self.componentType.getPositionByName(name) @@ -2180,15 +2360,16 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. verifyConstraints: :class:`bool` - If `False`, skip constraints validation + If :obj:`False`, skip constraints validation matchTags: :class:`bool` - If `False`, skip component tags matching + If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` - If `False`, skip component constraints matching + If :obj:`False`, skip component constraints matching Returns ------- @@ -2226,9 +2407,10 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): object instead of the requested component. instantiate: :class:`bool` - If `True` (default), inner component will be automatically instantiated. - If 'False' either existing component or the `noValue` object will be - returned. + If :obj:`True` (default), inner component will be automatically + instantiated. + If :obj:`False` either existing component or the :class:`NoValue` + object will be returned. Returns ------- @@ -2275,7 +2457,11 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): s.getComponentByPosition(0, instantiate=False) """ try: - componentValue = self._componentValues[idx] + if self._componentValues is noValue: + componentValue = noValue + + else: + componentValue = self._componentValues[idx] except IndexError: componentValue = noValue @@ -2317,15 +2503,16 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. verifyConstraints : :class:`bool` - If `False`, skip constraints validation + If :obj:`False`, skip constraints validation matchTags: :class:`bool` - If `False`, skip component tags matching + If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` - If `False`, skip component constraints matching + If :obj:`False`, skip component constraints matching Returns ------- @@ -2334,8 +2521,14 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): componentType = self.componentType componentTypeLen = self._componentTypeLen + if self._componentValues is noValue: + componentValues = [] + + else: + componentValues = self._componentValues + try: - currentValue = self._componentValues[idx] + currentValue = componentValues[idx] except IndexError: currentValue = noValue @@ -2343,12 +2536,12 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): if componentTypeLen < idx: raise error.PyAsn1Error('component index out of range') - self._componentValues = [noValue] * componentTypeLen + componentValues = [noValue] * componentTypeLen if value is noValue: if componentTypeLen: value = componentType.getTypeByPosition(idx) - if isinstance(value, base.AbstractConstructedAsn1Item): + if isinstance(value, base.ConstructedAsn1Type): value = value.clone(cloneValueFlag=componentType[idx].isDefaulted) elif currentValue is noValue: @@ -2357,13 +2550,13 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): elif not isinstance(value, base.Asn1Item): if componentTypeLen: subComponentType = componentType.getTypeByPosition(idx) - if isinstance(subComponentType, base.AbstractSimpleAsn1Item): + if isinstance(subComponentType, base.SimpleAsn1Type): value = subComponentType.clone(value=value) else: raise error.PyAsn1Error('%s can cast only scalar values' % componentType.__class__.__name__) - elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item): + elif currentValue is not noValue and isinstance(currentValue, base.SimpleAsn1Type): value = currentValue.clone(value=value) else: @@ -2389,32 +2582,35 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): raise exType('%s at %s' % (exValue, self.__class__.__name__)) if componentTypeLen or idx in self._dynamicNames: - self._componentValues[idx] = value + componentValues[idx] = value - elif len(self._componentValues) == idx: - self._componentValues.append(value) + elif len(componentValues) == idx: + componentValues.append(value) self._dynamicNames.addField(idx) else: raise error.PyAsn1Error('Component index out of range') + self._componentValues = componentValues + return self @property def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. - If *isValue* is `False` then this object represents just ASN.1 schema. + If *isValue* is :obj:`False` then this object represents just ASN.1 schema. - If *isValue* is `True` then, in addition to its ASN.1 schema features, - this object can also be used like a Python built-in object (e.g. `int`, - `str`, `dict` etc.). + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, + this object can also be used like a Python built-in object (e.g. + :class:`int`, :class:`str`, :class:`dict` etc.). Returns ------- : :class:`bool` - :class:`False` if object represents just ASN.1 schema. - :class:`True` if object represents ASN.1 schema and can be used as a normal value. + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a + normal value. Note ---- @@ -2426,7 +2622,16 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): The PyASN1 value objects can **additionally** participate in many operations involving regular Python objects (e.g. arithmetic, comprehension etc). + + It is sufficient for |ASN.1| objects to have all non-optional and non-defaulted + components being value objects to be considered as a value objects as a whole. + In other words, even having one or more optional components not turned into + value objects, |ASN.1| object is still considered as a value object. Defaulted + components are normally value objects by default. """ + if self._componentValues is noValue: + return False + componentType = self.componentType if componentType: @@ -2497,7 +2702,6 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): if self._componentTypeLen: return self.componentType[idx].name - class Sequence(SequenceAndSetBase): __doc__ = SequenceAndSetBase.__doc__ @@ -2583,9 +2787,10 @@ class Set(SequenceAndSetBase): object instead of the requested component. instantiate: :class:`bool` - If `True` (default), inner component will be automatically instantiated. - If 'False' either existing component or the `noValue` object will be - returned. + If :obj:`True` (default), inner component will be automatically + instantiated. + If :obj:`False` either existing component or the :class:`noValue` + object will be returned. Returns ------- @@ -2621,18 +2826,19 @@ class Set(SequenceAndSetBase): value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. verifyConstraints : :class:`bool` - If `False`, skip constraints validation + If :obj:`False`, skip constraints validation matchTags: :class:`bool` - If `False`, skip component tags matching + If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` - If `False`, skip component constraints matching + If :obj:`False`, skip component constraints matching innerFlag: :class:`bool` - If `True`, search for matching *tagSet* recursively. + If :obj:`True`, search for matching *tagSet* recursively. Returns ------- @@ -2664,9 +2870,10 @@ class Set(SequenceAndSetBase): class Choice(Set): - """Create |ASN.1| type. + """Create |ASN.1| schema or value object. - |ASN.1| objects are mutable and duck-type Python :class:`dict` objects. + |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, + its objects are mutable and duck-type Python :class:`list` objects. Keyword Args ------------ @@ -2811,7 +3018,7 @@ class Choice(Set): tagSet = component.effectiveTagSet else: tagSet = component.tagSet - if isinstance(component, base.AbstractConstructedAsn1Item): + if isinstance(component, base.ConstructedAsn1Type): myClone.setComponentByType( tagSet, component.clone(cloneValueFlag=cloneValueFlag) ) @@ -2849,15 +3056,16 @@ class Choice(Set): A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. Once a new value is set to *idx* component, previous value is dropped. + If `value` is not given, schema object will be set as a component. verifyConstraints : :class:`bool` - If `False`, skip constraints validation + If :obj:`False`, skip constraints validation matchTags: :class:`bool` - If `False`, skip component tags matching + If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` - If `False`, skip component constraints matching + If :obj:`False`, skip component constraints matching Returns ------- @@ -2927,17 +3135,18 @@ class Choice(Set): def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. - If *isValue* is `False` then this object represents just ASN.1 schema. + If *isValue* is :obj:`False` then this object represents just ASN.1 schema. - If *isValue* is `True` then, in addition to its ASN.1 schema features, - this object can also be used like a Python built-in object (e.g. `int`, - `str`, `dict` etc.). + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, + this object can also be used like a Python built-in object (e.g. + :class:`int`, :class:`str`, :class:`dict` etc.). Returns ------- : :class:`bool` - :class:`False` if object represents just ASN.1 schema. - :class:`True` if object represents ASN.1 schema and can be used as a normal value. + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a normal + value. Note ---- @@ -2959,7 +3168,7 @@ class Choice(Set): def clear(self): self._currentIdx = None - Set.clear(self) + return Set.clear(self) # compatibility stubs @@ -2970,16 +3179,19 @@ class Choice(Set): class Any(OctetString): """Create |ASN.1| schema or value object. - |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3 - :class:`bytes`. When used in Unicode context, |ASN.1| type assumes "|encoding|" - serialisation. + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, + its objects are immutable and duck-type Python 2 :class:`str` or Python 3 + :class:`bytes`. When used in Unicode context, |ASN.1| type assumes + "|encoding|" serialisation. Keyword Args ------------ - value: :class:`str`, :class:`bytes` or |ASN.1| object - string (Python 2) or bytes (Python 3), alternatively unicode object - (Python 2) or string (Python 3) representing character string to be - serialised into octets (note `encoding` parameter) or |ASN.1| object. + value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object + :class:`str` (Python 2) or :class:`bytes` (Python 3), alternatively + :class:`unicode` object (Python 2) or :class:`str` (Python 3) + representing character string to be serialised into octets (note + `encoding` parameter) or |ASN.1| object. + If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) @@ -3002,7 +3214,7 @@ class Any(OctetString): Raises ------ - :py:class:`~pyasn1.error.PyAsn1Error` + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples |