diff options
-rw-r--r-- | CHANGES.rst | 9 | ||||
-rw-r--r-- | docs/source/pyasn1/type/base/constructedasn1type.rst | 2 | ||||
-rw-r--r-- | docs/source/pyasn1/type/univ/choice.rst | 4 | ||||
-rw-r--r-- | docs/source/pyasn1/type/univ/sequence.rst | 4 | ||||
-rw-r--r-- | docs/source/pyasn1/type/univ/sequenceof.rst | 4 | ||||
-rw-r--r-- | docs/source/pyasn1/type/univ/set.rst | 4 | ||||
-rw-r--r-- | docs/source/pyasn1/type/univ/setof.rst | 4 | ||||
-rw-r--r-- | pyasn1/type/base.py | 31 | ||||
-rw-r--r-- | pyasn1/type/char.py | 4 | ||||
-rw-r--r-- | pyasn1/type/univ.py | 109 | ||||
-rw-r--r-- | tests/type/test_univ.py | 20 |
11 files changed, 101 insertions, 94 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 0dcb2f2..f9c00bb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,15 @@ Revision 0.4.7, released XX-09-2019 purpose e.g. ensuring all required fields are in a good shape. By default this check invokes subtype constraints verification and is run by codecs on value de/serialisation. +- Deprecate `subtypeSpec` attributes and keyword argument. It is now + recommended to pass `ValueSizeConstraint`, as well as all other constraints, + to `subtypeSpec`. +- Fixed a design bug in a way of how the items assigned to constructed + types are verified. Now if `Asn1Type`-based object is assigned, its + compatibility is verified based on having all tags and constraint + objects as the type in field definition. When a bare Python value is + assigned, then field type object is cloned and initialized with the + bare value (constraints verificaton would run at this moment). Revision 0.4.6, released 31-07-2019 ----------------------------------- diff --git a/docs/source/pyasn1/type/base/constructedasn1type.rst b/docs/source/pyasn1/type/base/constructedasn1type.rst index 169552f..cf7f665 100644 --- a/docs/source/pyasn1/type/base/constructedasn1type.rst +++ b/docs/source/pyasn1/type/base/constructedasn1type.rst @@ -6,5 +6,5 @@ |ASN.1| type ------------ -.. autoclass:: pyasn1.type.base.ConstructedAsn1Type(tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection(), componentType=None) +.. autoclass:: pyasn1.type.base.ConstructedAsn1Type(tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), componentType=None) :members: isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, isInconsistent diff --git a/docs/source/pyasn1/type/univ/choice.rst b/docs/source/pyasn1/type/univ/choice.rst index 9e2b4dc..323932b 100644 --- a/docs/source/pyasn1/type/univ/choice.rst +++ b/docs/source/pyasn1/type/univ/choice.rst @@ -6,8 +6,8 @@ |ASN.1| type ------------ -.. autoclass:: pyasn1.type.univ.Choice(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection()) - :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, +.. autoclass:: pyasn1.type.univ.Choice(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection()) + :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents, getComponentByType, setComponentByType, getName, getComponent, isInconsistent diff --git a/docs/source/pyasn1/type/univ/sequence.rst b/docs/source/pyasn1/type/univ/sequence.rst index 94804c7..163a3a0 100644 --- a/docs/source/pyasn1/type/univ/sequence.rst +++ b/docs/source/pyasn1/type/univ/sequence.rst @@ -6,8 +6,8 @@ |ASN.1| type ------------ -.. autoclass:: pyasn1.type.univ.Sequence(componentType=NamedTypes(), tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection()) - :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, getComponentByPosition, +.. autoclass:: pyasn1.type.univ.Sequence(componentType=NamedTypes(), tagSet=tagSet(), subtypeSpec=ConstraintsIntersection()) + :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents, clear, reset, isInconsistent diff --git a/docs/source/pyasn1/type/univ/sequenceof.rst b/docs/source/pyasn1/type/univ/sequenceof.rst index be5442e..dd4f0c5 100644 --- a/docs/source/pyasn1/type/univ/sequenceof.rst +++ b/docs/source/pyasn1/type/univ/sequenceof.rst @@ -6,8 +6,8 @@ |ASN.1| type ------------ -.. autoclass:: pyasn1.type.univ.SequenceOf(componentType=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection()) - :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, +.. autoclass:: pyasn1.type.univ.SequenceOf(componentType=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection()) + :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, getComponentByPosition, setComponentByPosition, clear, reset, isInconsistent .. note:: diff --git a/docs/source/pyasn1/type/univ/set.rst b/docs/source/pyasn1/type/univ/set.rst index aad8ee0..157fed0 100644 --- a/docs/source/pyasn1/type/univ/set.rst +++ b/docs/source/pyasn1/type/univ/set.rst @@ -6,8 +6,8 @@ |ASN.1| type ------------ -.. autoclass:: pyasn1.type.univ.Set(componentType=NamedTypes(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection()) - :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, +.. autoclass:: pyasn1.type.univ.Set(componentType=NamedTypes(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection()) + :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents, getComponentByType, setComponentByType, clear, reset, isInconsistent diff --git a/docs/source/pyasn1/type/univ/setof.rst b/docs/source/pyasn1/type/univ/setof.rst index ce519a9..581c20f 100644 --- a/docs/source/pyasn1/type/univ/setof.rst +++ b/docs/source/pyasn1/type/univ/setof.rst @@ -6,8 +6,8 @@ |ASN.1| type ------------ -.. autoclass:: pyasn1.type.univ.SetOf(componentType=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection()) - :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, +.. autoclass:: pyasn1.type.univ.SetOf(componentType=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection()) + :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, Â getComponentByPosition, setComponentByPosition, clear, reset, isInconsistent .. note:: diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py index 1fd69f9..834b76e 100644 --- a/pyasn1/type/base.py +++ b/pyasn1/type/base.py @@ -498,17 +498,39 @@ class ConstructedAsn1Type(Asn1Type): strictConstraints = False componentType = None - sizeSpec = None + + # backward compatibility, unused + sizeSpec = constraint.ConstraintsIntersection() def __init__(self, **kwargs): readOnly = { 'componentType': self.componentType, + # backward compatibility, unused 'sizeSpec': self.sizeSpec } + + # backward compatibility: preserve legacy sizeSpec support + kwargs = self._moveSizeSpec(**kwargs) + readOnly.update(kwargs) Asn1Type.__init__(self, **readOnly) + def _moveSizeSpec(self, **kwargs): + # backward compatibility, unused + sizeSpec = kwargs.pop('sizeSpec', self.sizeSpec) + if sizeSpec: + subtypeSpec = kwargs.pop('subtypeSpec', self.subtypeSpec) + if subtypeSpec: + subtypeSpec = sizeSpec + + else: + subtypeSpec += sizeSpec + + kwargs['subtypeSpec'] = subtypeSpec + + return kwargs + def __repr__(self): representation = '%s %s object' % ( self.__class__.__name__, self.isValue and 'value' or 'schema' @@ -667,7 +689,7 @@ class ConstructedAsn1Type(Asn1Type): :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found """ try: - self.sizeSpec(self) + self.subtypeSpec(self) except error.PyAsn1Error: exc = sys.exc_info()[1] @@ -694,9 +716,10 @@ class ConstructedAsn1Type(Asn1Type): def getComponentType(self): return self.componentType + # backward compatibility, unused def verifySizeSpec(self): - self.sizeSpec(self) + self.subtypeSpec(self) -# Backward compatibility + # Backward compatibility AbstractConstructedAsn1Item = ConstructedAsn1Type diff --git a/pyasn1/type/char.py b/pyasn1/type/char.py index 3f8c444..06074da 100644 --- a/pyasn1/type/char.py +++ b/pyasn1/type/char.py @@ -39,7 +39,9 @@ class AbstractCharacterString(univ.OctetString): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. encoding: :py:class:`str` Unicode codec ID to encode/decode :class:`unicode` (Python 2) or diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py index 4f305f6..fbf8ed5 100644 --- a/pyasn1/type/univ.py +++ b/pyasn1/type/univ.py @@ -47,7 +47,9 @@ class Integer(base.SimpleAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers @@ -293,7 +295,9 @@ class Boolean(Integer): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s).Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers @@ -377,7 +381,9 @@ class BitString(base.SimpleAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers @@ -747,7 +753,9 @@ class OctetString(base.SimpleAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. encoding: :py:class:`str` Unicode codec ID to encode/decode :class:`unicode` (Python 2) or @@ -1130,7 +1138,9 @@ class ObjectIdentifier(base.SimpleAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. Raises ------ @@ -1268,7 +1278,9 @@ class Real(base.SimpleAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. Raises ------ @@ -1552,7 +1564,9 @@ class Enumerated(Integer): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers @@ -1620,10 +1634,9 @@ class SequenceOfAndSetOfBase(base.ConstructedAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) - - sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing collection size constraint + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type can only occur on explicit + `.isInconsistent` call. Examples -------- @@ -1645,7 +1658,7 @@ class SequenceOfAndSetOfBase(base.ConstructedAsn1Type): # support positional params for backward compatibility if args: for key, value in zip(('componentType', 'tagSet', - 'subtypeSpec', 'sizeSpec'), args): + 'subtypeSpec'), args): if key in kwargs: raise error.PyAsn1Error('Conflicting positional and keyword params!') kwargs['componentType'] = value @@ -1921,7 +1934,8 @@ class SequenceOfAndSetOfBase(base.ConstructedAsn1Type): componentType.isSameTypeWith or componentType.isSuperTypeOf) - if not subtypeChecker(value, matchTags, matchConstraints): + if not subtypeChecker(value, verifyConstraints and matchTags, + verifyConstraints and matchConstraints): # TODO: we should wrap componentType with UnnamedType to carry # additional properties associated with componentType if componentType.typeId != Any.typeId: @@ -1929,21 +1943,6 @@ class SequenceOfAndSetOfBase(base.ConstructedAsn1Type): '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 verifyConstraints and value.isValue: - try: - self.subtypeSpec(value, idx) - - except error.PyAsn1Error: - exType, exValue, exTb = sys.exc_info() - raise exType('%s at %s' % (exValue, self.__class__.__name__)) - componentValues[idx] = value self._componentValues = componentValues @@ -2063,10 +2062,6 @@ class SequenceOf(SequenceOfAndSetOfBase): #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() - #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - #: object imposing size constraint on |ASN.1| objects - sizeSpec = constraint.ConstraintsIntersection() - # Disambiguation ASN.1 types identification typeId = SequenceOfAndSetOfBase.getTypeId() @@ -2090,10 +2085,6 @@ class SetOf(SequenceOfAndSetOfBase): #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() - #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - #: object imposing size constraint on |ASN.1| objects - sizeSpec = constraint.ConstraintsIntersection() - # Disambiguation ASN.1 types identification typeId = SequenceOfAndSetOfBase.getTypeId() @@ -2113,10 +2104,9 @@ class SequenceAndSetBase(base.ConstructedAsn1Type): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) - - sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing collection size constraint + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type can only occur on explicit + `.isInconsistent` call. Examples -------- @@ -2562,25 +2552,19 @@ class SequenceAndSetBase(base.ConstructedAsn1Type): else: raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__) - elif (matchTags or matchConstraints) and componentTypeLen: + elif ((verifyConstraints or matchTags or matchConstraints) and + componentTypeLen): subComponentType = componentType.getTypeByPosition(idx) if subComponentType is not noValue: subtypeChecker = (self.strictConstraints and subComponentType.isSameTypeWith or subComponentType.isSuperTypeOf) - if not subtypeChecker(value, matchTags, matchConstraints): + if not subtypeChecker(value, verifyConstraints and matchTags, + verifyConstraints and matchConstraints): if not componentType[idx].openType: raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) - if verifyConstraints and value.isValue: - try: - self.subtypeSpec(value, idx) - - except error.PyAsn1Error: - exType, exValue, exTb = sys.exc_info() - raise exType('%s at %s' % (exValue, self.__class__.__name__)) - if componentTypeLen or idx in self._dynamicNames: componentValues[idx] = value @@ -2717,10 +2701,6 @@ class Sequence(SequenceAndSetBase): #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() - #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - #: object imposing constraints on |ASN.1| objects - sizeSpec = constraint.ConstraintsIntersection() - #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) #: object imposing size constraint on |ASN.1| objects componentType = namedtype.NamedTypes() @@ -2760,10 +2740,6 @@ class Set(SequenceAndSetBase): #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() - #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - #: object imposing constraints on |ASN.1| objects - sizeSpec = constraint.ConstraintsIntersection() - # Disambiguation ASN.1 types identification typeId = SequenceAndSetBase.getTypeId() @@ -2884,10 +2860,9 @@ class Choice(Set): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) - - sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing collection size constraint + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type can only occur on explicit + `.isInconsistent` call. Examples -------- @@ -2927,11 +2902,7 @@ class Choice(Set): #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. - subtypeSpec = constraint.ConstraintsIntersection() - - #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - #: object imposing size constraint on |ASN.1| objects - sizeSpec = constraint.ConstraintsIntersection( + subtypeSpec = constraint.ConstraintsIntersection( constraint.ValueSizeConstraint(1, 1) ) @@ -3197,7 +3168,9 @@ class Any(OctetString): Object representing non-default ASN.1 tag(s) subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` - Object representing non-default ASN.1 subtype constraint(s) + Object representing non-default ASN.1 subtype constraint(s). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. encoding: :py:class:`str` Unicode codec ID to encode/decode :class:`unicode` (Python 2) or diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py index 4cbfa01..d9f921b 100644 --- a/tests/type/test_univ.py +++ b/tests/type/test_univ.py @@ -992,11 +992,13 @@ class SequenceOf(BaseTestCase): assert self.s1 == self.s2, '__cmp__() fails' def testSubtypeSpec(self): - s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion( - constraint.SingleValueConstraint(str2octs('abc')) - )) + s = self.s1.clone( + componentType=univ.OctetString().subtype( + subtypeSpec=constraint.SingleValueConstraint(str2octs('abc')))) try: - s.setComponentByPosition(0, univ.OctetString('abc')) + s.setComponentByPosition( + 0, univ.OctetString().subtype( + 'abc', subtypeSpec=constraint.SingleValueConstraint(str2octs('abc')))) except PyAsn1Error: assert 0, 'constraint fails' try: @@ -1006,7 +1008,7 @@ class SequenceOf(BaseTestCase): s.setComponentByPosition(1, univ.OctetString('Abc'), verifyConstraints=False) except PyAsn1Error: - assert 0, 'constraint failes with verifyConstraints=True' + assert 0, 'constraint fails with verifyConstraints=False' else: assert 0, 'constraint fails' @@ -1041,7 +1043,7 @@ class SequenceOf(BaseTestCase): pass def testConsistency(self): - s = self.s1.clone(sizeSpec=constraint.ConstraintsUnion( + s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion( constraint.ValueSizeConstraint(1, 1) )) s.setComponentByPosition(0, univ.OctetString('abc')) @@ -1057,15 +1059,13 @@ class SequenceOf(BaseTestCase): def testSubtype(self): subtype = self.s1.subtype( implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2), - subtypeSpec=constraint.SingleValueConstraint(1, 3), - sizeSpec=constraint.ValueSizeConstraint(0, 1) + subtypeSpec=constraint.ValueSizeConstraint(0, 1) ) subtype.clear() clone = self.s1.clone( tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2)), - subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1, 3)), - sizeSpec=constraint.ValueSizeConstraint(0, 1) + subtypeSpec=constraint.ValueSizeConstraint(0, 1) ) clone.clear() assert clone == subtype |