diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-11-16 22:32:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-16 22:32:14 +0100 |
commit | 4cfceb7716d5f553643ad4509de1808989f0fc4d (patch) | |
tree | 0b62df991c5487f95818e9bc37e0eab0a23bae73 | |
parent | 9ec03d3837d3333f0edb2a0f9919dcf82c1f679c (diff) | |
download | pyasn1-git-4cfceb7716d5f553643ad4509de1808989f0fc4d.tar.gz |
Add default to .getComponentBy*() (#100)
* `default` kwarg added to .getComponentBy*() methods
-rw-r--r-- | CHANGES.rst | 2 | ||||
-rw-r--r-- | pyasn1/type/univ.py | 85 | ||||
-rw-r--r-- | tests/type/test_univ.py | 71 |
3 files changed, 140 insertions, 18 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 14337d5..1db5a70 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,8 @@ Revision 0.4.1, released XX-10-2017 or a Python value plus ASN.1 schema - BitString decoder optimised for better performance when running on constructed encoding +- Constructed types' .getComponentBy*() methods accept the `default` + parameter to return instead if schema object is to be returned - Constructed types' .getComponentBy*() methods accept the `instantiate` parameter to disable automatic inner component instantiation - Fixed Choice.clear() to fully reset internal state of the object diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py index a9cc8c0..9f66833 100644 --- a/pyasn1/type/univ.py +++ b/pyasn1/type/univ.py @@ -1685,7 +1685,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): else: myClone.setComponentByPosition(idx, componentValue.clone()) - def getComponentByPosition(self, idx, instantiate=True): + def getComponentByPosition(self, idx, default=noValue, instantiate=True): """Return |ASN.1| type component value by position. Equivalent to Python sequence subscription operation (e.g. `[]`). @@ -1700,6 +1700,10 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): Keyword Args ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + 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 @@ -1721,6 +1725,14 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): s = MySequenceOf() + # returns component #0 with `.isValue` property False + s.getComponentByPosition(0) + + # returns None + s.getComponentByPosition(0, default=None) + + s.clear() + # returns noValue s.getComponentByPosition(0, instantiate=False) @@ -1740,15 +1752,20 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): s.getComponentByPosition(0, instantiate=False) """ try: - return self._componentValues[idx] + componentValue = self._componentValues[idx] except IndexError: if not instantiate: - return noValue + return default self.setComponentByPosition(idx) - return self._componentValues[idx] + componentValue = self._componentValues[idx] + + if default is noValue or componentValue.isValue: + return componentValue + else: + return default def setComponentByPosition(self, idx, value=noValue, verifyConstraints=True, @@ -2124,7 +2141,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): else: myClone.setComponentByPosition(idx, componentValue.clone()) - def getComponentByName(self, name, instantiate=True): + def getComponentByName(self, name, default=noValue, instantiate=True): """Returns |ASN.1| type component by name. Equivalent to Python :class:`dict` subscription operation (e.g. `[]`). @@ -2136,6 +2153,10 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): Keyword Args ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + 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 @@ -2155,7 +2176,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): except KeyError: raise error.PyAsn1Error('Name %s not found' % (name,)) - return self.getComponentByPosition(idx, instantiate) + return self.getComponentByPosition(idx, default=default, instantiate=instantiate) def setComponentByName(self, name, value=noValue, verifyConstraints=True, @@ -2202,20 +2223,24 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): idx, value, verifyConstraints, matchTags, matchConstraints ) - def getComponentByPosition(self, idx, instantiate=True): + def getComponentByPosition(self, idx, default=noValue, instantiate=True): """Returns |ASN.1| type component by index. Equivalent to Python sequence subscription operation (e.g. `[]`). Parameters ---------- - idx : :class:`int` + idx: :class:`int` Component index (zero-based). Must either refer to an existing component or (if *componentType* is set) new ASN.1 schema object gets instantiated. Keyword Args ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + 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 @@ -2239,6 +2264,14 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): s = MySequence() + # returns component #0 with `.isValue` property False + s.getComponentByPosition(0) + + # returns None + s.getComponentByPosition(0, default=None) + + s.clear() + # returns noValue s.getComponentByPosition(0, instantiate=False) @@ -2259,16 +2292,25 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): """ try: componentValue = self._componentValues[idx] + except IndexError: componentValue = noValue if not instantiate: - return componentValue + if componentValue is noValue or not componentValue.isValue: + return default + else: + return componentValue if componentValue is noValue: self.setComponentByPosition(idx) - return self._componentValues[idx] + componentValue = self._componentValues[idx] + + if default is noValue or componentValue.isValue: + return componentValue + else: + return default def setComponentByPosition(self, idx, value=noValue, verifyConstraints=True, @@ -2523,7 +2565,8 @@ class Set(SequenceAndSetBase): def getComponent(self, innerFlag=False): return self - def getComponentByType(self, tagSet, instantiate=True, innerFlag=False): + def getComponentByType(self, tagSet, default=noValue, + instantiate=True, innerFlag=False): """Returns |ASN.1| type component by ASN.1 tag. Parameters @@ -2534,6 +2577,10 @@ class Set(SequenceAndSetBase): Keyword Args ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + 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 @@ -2544,15 +2591,16 @@ class Set(SequenceAndSetBase): : :py:class:`~pyasn1.type.base.PyAsn1Item` a pyasn1 object """ - component = self.getComponentByPosition( - self.componentType.getPositionByType(tagSet), instantiate + componentValue = self.getComponentByPosition( + self.componentType.getPositionByType(tagSet), + default=default, instantiate=instantiate ) - if innerFlag and isinstance(component, Set): + if innerFlag and isinstance(componentValue, Set): # get inner component by inner tagSet - return component.getComponent(innerFlag=True) + return componentValue.getComponent(innerFlag=True) else: # get outer component by inner tagSet - return component + return componentValue def setComponentByType(self, tagSet, value=noValue, verifyConstraints=True, @@ -2769,11 +2817,12 @@ class Choice(Set): else: myClone.setComponentByType(tagSet, component.clone()) - def getComponentByPosition(self, idx, instantiate=True): + def getComponentByPosition(self, idx, default=noValue, instantiate=True): __doc__ = Set.__doc__ if self._currentIdx is None or self._currentIdx != idx: - return Set.getComponentByPosition(self, idx, instantiate) + return Set.getComponentByPosition(self, idx, default=default, + instantiate=instantiate) return self._componentValues[idx] diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py index 57fec99..7c3162a 100644 --- a/tests/type/test_univ.py +++ b/tests/type/test_univ.py @@ -1139,6 +1139,19 @@ class SequenceOf(BaseTestCase): assert n == o + def testGetComponentWithDefault(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString() + + s = SequenceOf() + assert s.getComponentByPosition(0, default=None, instantiate=False) is None + assert s.getComponentByPosition(0, default=None) is None + s[0] = 'test' + assert s.getComponentByPosition(0, default=None) is not None + assert s.getComponentByPosition(0, default=None) == str2octs('test') + s.clear() + assert s.getComponentByPosition(0, default=None) is None def testGetComponentNoInstantiation(self): @@ -1376,6 +1389,25 @@ class Sequence(BaseTestCase): s['name'] = 'abc' assert s['name'] == str2octs('abc') + def testGetComponentWithDefault(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('nick', univ.OctetString()), + ) + + s = Sequence() + + assert s[0] == str2octs('') + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByName('nick', default=None) is None + s[1] = 'test' + assert s.getComponentByPosition(1, default=None) is not None + assert s.getComponentByPosition(1, default=None) == str2octs('test') + s.clear() + assert s.getComponentByPosition(1, default=None) is None + def testGetComponentNoInstantiation(self): class Sequence(univ.Sequence): @@ -1619,6 +1651,24 @@ class Set(BaseTestCase): s['name'] = 'abc' assert s['name'] == str2octs('abc') + def testGetComponentWithDefault(self): + + class Set(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer(123)), + namedtype.OptionalNamedType('nick', univ.OctetString()), + ) + + s = Set() + assert s[0] == 123 + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByName('nick', default=None) is None + s[1] = 'test' + assert s.getComponentByPosition(1, default=None) is not None + assert s.getComponentByPosition(1, default=None) == str2octs('test') + s.clear() + assert s.getComponentByPosition(1, default=None) is None + def testGetComponentNoInstantiation(self): class Set(univ.Set): @@ -1792,6 +1842,27 @@ class Choice(BaseTestCase): c.setComponentByType(univ.OctetString.tagSet, 'abc') assert c.getName() == 'name' + def testGetComponentWithDefault(self): + + s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('id', univ.Integer()) + ) + ) + + assert s.getComponentByPosition(0, default=None, instantiate=False) is None + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByName('name', default=None, instantiate=False) is None + assert s.getComponentByName('id', default=None, instantiate=False) is None + assert s.getComponentByType(univ.OctetString.tagSet, default=None) is None + assert s.getComponentByType(univ.Integer.tagSet, default=None) is None + s[1] = 123 + assert s.getComponentByPosition(1, default=None) is not None + assert s.getComponentByPosition(1, univ.noValue) == 123 + s.clear() + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + def testGetComponentNoInstantiation(self): s = univ.Choice( |