Merge qt-r1-dev-plus-aosp-without-vendor (5817612) into stage-aosp-master

No content change.

Bug: 135460123
Change-Id: If61fe3d45c72de1182a493e8405c99bb223b79e5
Merged-In: I231c58c0cdb60b8e7dcf9c017d247ee6f9f01c75
diff --git a/CHANGES.rst b/CHANGES.rst
index 0ca5904..139376d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,4 +1,26 @@
 
+Revision 0.4.7, released 01-09-2019
+-----------------------------------
+
+- Added `isInconsistent` property to all constructed types. This property
+  conceptually replaces `verifySizeSpec` method to serve a more general
+  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).
+- Added `WithComponentsConstraint` along with related
+  `ComponentPresentConstraint` and `ComponentAbsentConstraint` classes
+  to be used with `Sequence`/`Set` types representing
+  `SET ... WITH COMPONENTS ...` like ASN.1 constructs.
+
 Revision 0.4.6, released 31-07-2019
 -----------------------------------
 
diff --git a/METADATA b/METADATA
index 258fbfd..b640262 100644
--- a/METADATA
+++ b/METADATA
@@ -9,10 +9,10 @@
     type: GIT
     value: "https://github.com/etingof/pyasn1"
   }
-  version: "v0.4.6"
+  version: "v0.4.7"
   last_upgrade_date {
     year: 2019
-    month: 7
-    day: 31
+    month: 9
+    day: 3
   }
 }
diff --git a/docs/source/pyasn1/type/base/constructedasn1type.rst b/docs/source/pyasn1/type/base/constructedasn1type.rst
index 54d828f..8709066 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
diff --git a/docs/source/pyasn1/type/constraint/contents.rst b/docs/source/pyasn1/type/constraint/contents.rst
index 8e4db7c..a4e2424 100644
--- a/docs/source/pyasn1/type/constraint/contents.rst
+++ b/docs/source/pyasn1/type/constraint/contents.rst
@@ -32,6 +32,7 @@
    /pyasn1/type/constraint/valuerange
    /pyasn1/type/constraint/valuesize
    /pyasn1/type/constraint/permittedalphabet
+   /pyasn1/type/constraint/withcomponents
 
 
 Logic operations on constraints
diff --git a/docs/source/pyasn1/type/constraint/withcomponents.rst b/docs/source/pyasn1/type/constraint/withcomponents.rst
new file mode 100644
index 0000000..f1556b0
--- /dev/null
+++ b/docs/source/pyasn1/type/constraint/withcomponents.rst
@@ -0,0 +1,16 @@
+
+.. _constrain.WithComponentsConstraint:
+
+.. |Constraint| replace:: WithComponentsConstraint
+
+WITH COMPONENTS constraint
+--------------------------
+
+.. autoclass:: pyasn1.type.constraint.WithComponentsConstraint(*fields)
+   :members:
+
+.. autoclass:: pyasn1.type.constraint.ComponentPresentConstraint()
+   :members:
+
+.. autoclass:: pyasn1.type.constraint.ComponentAbsentConstraint()
+   :members:
diff --git a/docs/source/pyasn1/type/univ/choice.rst b/docs/source/pyasn1/type/univ/choice.rst
index 9731971..323932b 100644
--- a/docs/source/pyasn1/type/univ/choice.rst
+++ b/docs/source/pyasn1/type/univ/choice.rst
@@ -6,10 +6,10 @@
 |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
+             getComponentByType, setComponentByType, getName, getComponent, isInconsistent
 
    .. note::
 
diff --git a/docs/source/pyasn1/type/univ/sequence.rst b/docs/source/pyasn1/type/univ/sequence.rst
index 0988004..163a3a0 100644
--- a/docs/source/pyasn1/type/univ/sequence.rst
+++ b/docs/source/pyasn1/type/univ/sequence.rst
@@ -6,10 +6,10 @@
 |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
+             clear, reset, isInconsistent
 
    .. note::
 
diff --git a/docs/source/pyasn1/type/univ/sequenceof.rst b/docs/source/pyasn1/type/univ/sequenceof.rst
index 6a09b92..dd4f0c5 100644
--- a/docs/source/pyasn1/type/univ/sequenceof.rst
+++ b/docs/source/pyasn1/type/univ/sequenceof.rst
@@ -6,9 +6,9 @@
 |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,
-             getComponentByPosition, setComponentByPosition, clear, reset
+.. 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 792d2e9..157fed0 100644
--- a/docs/source/pyasn1/type/univ/set.rst
+++ b/docs/source/pyasn1/type/univ/set.rst
@@ -6,10 +6,10 @@
 |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
+             getComponentByType, setComponentByType, clear, reset, isInconsistent
 
    .. note::
 
diff --git a/docs/source/pyasn1/type/univ/setof.rst b/docs/source/pyasn1/type/univ/setof.rst
index 67ef92f..581c20f 100644
--- a/docs/source/pyasn1/type/univ/setof.rst
+++ b/docs/source/pyasn1/type/univ/setof.rst
@@ -6,9 +6,9 @@
 |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,
-             getComponentByPosition, setComponentByPosition, clear, reset
+.. 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/__init__.py b/pyasn1/__init__.py
index d780df2..92838e0 100644
--- a/pyasn1/__init__.py
+++ b/pyasn1/__init__.py
@@ -1,7 +1,7 @@
 import sys
 
 # https://www.python.org/dev/peps/pep-0396/
-__version__ = '0.4.6'
+__version__ = '0.4.7'
 
 if sys.version_info[:2] < (2, 4):
     raise RuntimeError('PyASN1 requires Python 2.4 or later')
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
index 3f2d180..5759ab8 100644
--- a/pyasn1/codec/ber/decoder.py
+++ b/pyasn1/codec/ber/decoder.py
@@ -695,7 +695,9 @@
                                 asn1Object.setComponentByPosition(idx, component)
 
             else:
-                asn1Object.verifySizeSpec()
+                inconsistency = asn1Object.isInconsistent
+                if inconsistency:
+                    raise inconsistency
 
         else:
             asn1Object = asn1Spec.clone()
@@ -879,7 +881,9 @@
                                     asn1Object.setComponentByPosition(idx, component)
 
                 else:
-                    asn1Object.verifySizeSpec()
+                    inconsistency = asn1Object.isInconsistent
+                    if inconsistency:
+                        raise inconsistency
 
         else:
             asn1Object = asn1Spec.clone()
diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py
index a5d5fd3..778aa86 100644
--- a/pyasn1/codec/ber/encoder.py
+++ b/pyasn1/codec/ber/encoder.py
@@ -537,7 +537,9 @@
 
         if asn1Spec is None:
             # instance of ASN.1 schema
-            value.verifySizeSpec()
+            inconsistency = value.isInconsistent
+            if inconsistency:
+                raise inconsistency
 
             namedTypes = value.componentType
 
@@ -643,7 +645,9 @@
     def _encodeComponents(self, value, asn1Spec, encodeFun, **options):
 
         if asn1Spec is None:
-            value.verifySizeSpec()
+            inconsistency = value.isInconsistent
+            if inconsistency:
+                raise inconsistency
 
         else:
             asn1Spec = asn1Spec.componentType
diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py
index 6a9db97..935b696 100644
--- a/pyasn1/codec/cer/encoder.py
+++ b/pyasn1/codec/cer/encoder.py
@@ -169,7 +169,9 @@
 
         if asn1Spec is None:
             # instance of ASN.1 schema
-            value.verifySizeSpec()
+            inconsistency = value.isInconsistent
+            if inconsistency:
+                raise inconsistency
 
             namedTypes = value.componentType
 
diff --git a/pyasn1/codec/native/encoder.py b/pyasn1/codec/native/encoder.py
index 4c5908d..4318abd 100644
--- a/pyasn1/codec/native/encoder.py
+++ b/pyasn1/codec/native/encoder.py
@@ -72,7 +72,9 @@
     protoDict = dict
 
     def encode(self, value, encodeFun, **options):
-        value.verifySizeSpec()
+        inconsistency = value.isInconsistent
+        if inconsistency:
+            raise inconsistency
 
         namedTypes = value.componentType
         substrate = self.protoDict()
@@ -90,7 +92,9 @@
 
 class SequenceOfEncoder(AbstractItemEncoder):
     def encode(self, value, encodeFun, **options):
-        value.verifySizeSpec()
+        inconsistency = value.isInconsistent
+        if inconsistency:
+            raise inconsistency
         return [encodeFun(x, **options) for x in value]
 
 
diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py
index 21e4041..994f1c9 100644
--- a/pyasn1/type/base.py
+++ b/pyasn1/type/base.py
@@ -498,17 +498,39 @@
     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'
@@ -655,9 +677,6 @@
 
         return clone
 
-    def verifySizeSpec(self):
-        self.sizeSpec(self)
-
     def getComponentByPosition(self, idx):
         raise error.PyAsn1Error('Method not implemented')
 
@@ -679,5 +698,10 @@
     def getComponentType(self):
         return self.componentType
 
-# Backward compatibility
+    # backward compatibility, unused
+    def verifySizeSpec(self):
+        self.subtypeSpec(self)
+
+
+        # 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 @@
         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/constraint.py b/pyasn1/type/constraint.py
index 807c827..b66627d 100644
--- a/pyasn1/type/constraint.py
+++ b/pyasn1/type/constraint.py
@@ -342,6 +342,151 @@
             raise error.ValueConstraintError(value)
 
 
+class ComponentPresentConstraint(AbstractConstraint):
+    """Create a ComponentPresentConstraint object.
+
+    The ComponentPresentConstraint is only satisfied when the value
+    is not `None`.
+
+    The ComponentPresentConstraint object is typically used with
+    `WithComponentsConstraint`.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        present = ComponentPresentConstraint()
+
+        # this will succeed
+        present('whatever')
+
+        # this will raise ValueConstraintError
+        present(None)
+    """
+    def _setValues(self, values):
+        self._values = ('<must be present>',)
+
+        if values:
+            raise error.PyAsn1Error('No arguments expected')
+
+    def _testValue(self, value, idx):
+        if value is None:
+            raise error.ValueConstraintError(
+                'Component is not present:')
+
+
+class ComponentAbsentConstraint(AbstractConstraint):
+    """Create a ComponentAbsentConstraint object.
+
+    The ComponentAbsentConstraint is only satisfied when the value
+    is `None`.
+
+    The ComponentAbsentConstraint object is typically used with
+    `WithComponentsConstraint`.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        absent = ComponentAbsentConstraint()
+
+        # this will succeed
+        absent(None)
+
+        # this will raise ValueConstraintError
+        absent('whatever')
+    """
+    def _setValues(self, values):
+        self._values = ('<must be absent>',)
+
+        if values:
+            raise error.PyAsn1Error('No arguments expected')
+
+    def _testValue(self, value, idx):
+        if value is not None:
+            raise error.ValueConstraintError(
+                'Component is not absent: %r' % value)
+
+
+class WithComponentsConstraint(AbstractConstraint):
+    """Create a WithComponentsConstraint object.
+
+    The `WithComponentsConstraint` satisfies any mapping object that has
+    constrained fields present or absent, what is indicated by
+    `ComponentPresentConstraint` and `ComponentAbsentConstraint`
+    objects respectively.
+
+    The `WithComponentsConstraint` object is typically applied
+    to  :class:`~pyasn1.type.univ.Set` or
+    :class:`~pyasn1.type.univ.Sequence` types.
+
+    Parameters
+    ----------
+    *fields: :class:`tuple`
+        Zero or more tuples of (`field`, `constraint`) indicating constrained
+        fields.
+
+    Notes
+    -----
+    On top of the primary use of `WithComponentsConstraint` (ensuring presence
+    or absence of particular components of a :class:`~pyasn1.type.univ.Set` or
+    :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other
+    constraint objects or their combinations. In case of scalar fields, these
+    constraints will be verified in addition to the constraints belonging to
+    scalar components themselves. However, formally, these additional
+    constraints do not change the type of these ASN.1 objects.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Item(Sequence):  #  Set is similar
+            '''
+            ASN.1 specification:
+
+            Item ::= SEQUENCE {
+                id    INTEGER OPTIONAL,
+                name  OCTET STRING OPTIONAL
+            } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT
+            '''
+            componentType = NamedTypes(
+                OptionalNamedType('id', Integer()),
+                OptionalNamedType('name', OctetString())
+            )
+            withComponents = ConstraintsUnion(
+                WithComponentsConstraint(
+                    ('id', ComponentPresentConstraint()),
+                    ('name', ComponentAbsentConstraint())
+                ),
+                WithComponentsConstraint(
+                    ('id', ComponentAbsentConstraint()),
+                    ('name', ComponentPresentConstraint())
+                )
+            )
+
+        item = Item()
+
+        # This will succeed
+        item['id'] = 1
+
+        # This will succeed
+        item.reset()
+        item['name'] = 'John'
+
+        # This will fail (on encoding)
+        item.reset()
+        descr['id'] = 1
+        descr['name'] = 'John'
+    """
+    def _testValue(self, value, idx):
+        for field, constraint in self._values:
+            constraint(value.get(field))
+
+    def _setValues(self, values):
+        AbstractConstraint._setValues(self, values)
+
+
 # This is a bit kludgy, meaning two op modes within a single constraint
 class InnerTypeConstraint(AbstractConstraint):
     """Value must satisfy the type and presence constraints"""
@@ -501,8 +646,8 @@
 class ConstraintsUnion(AbstractConstraintSet):
     """Create a ConstraintsUnion logic operator object.
 
-    The ConstraintsUnion logic operator only succeeds if
-    *at least a single* operand succeeds.
+    The ConstraintsUnion logic operator succeeds if
+    *at least* a single operand succeeds.
 
     The ConstraintsUnion object can be applied to
     any constraint and logic operator objects.
@@ -526,7 +671,7 @@
             CapitalOrSmall ::=
                 IA5String (FROM ("A".."Z") | FROM ("a".."z"))
             '''
-            subtypeSpec = ConstraintsIntersection(
+            subtypeSpec = ConstraintsUnion(
                 PermittedAlphabetConstraint('A', 'Z'),
                 PermittedAlphabetConstraint('a', 'z')
             )
diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py
index b39c533..aa688b2 100644
--- a/pyasn1/type/univ.py
+++ b/pyasn1/type/univ.py
@@ -47,7 +47,9 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         # 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 @@
                     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 @@
                         '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
@@ -2043,6 +2042,41 @@
 
         return True
 
+    @property
+    def isInconsistent(self):
+        """Run necessary checks to ensure |ASN.1| object consistency.
+
+        Default action is to verify |ASN.1| object against constraints imposed
+        by `subtypeSpec`.
+
+        Raises
+        ------
+        :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found
+        """
+        if self.componentType is noValue or not self.subtypeSpec:
+            return False
+
+        if self._componentValues is noValue:
+            return True
+
+        mapping = {}
+
+        for idx, value in self._componentValues.items():
+            # Absent fields are not in the mapping
+            if value is noValue:
+                continue
+
+            mapping[idx] = value
+
+        try:
+            # Represent SequenceOf/SetOf as a bare dict to constraints chain
+            self.subtypeSpec(mapping)
+
+        except error.PyAsn1Error:
+            exc = sys.exc_info()[1]
+            return exc
+
+        return False
 
 class SequenceOf(SequenceOfAndSetOfBase):
     __doc__ = SequenceOfAndSetOfBase.__doc__
@@ -2063,10 +2097,6 @@
     #: 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 +2120,6 @@
     #: 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 +2139,9 @@
         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 +2587,19 @@
             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
 
@@ -2653,6 +2672,44 @@
 
         return True
 
+    @property
+    def isInconsistent(self):
+        """Run necessary checks to ensure |ASN.1| object consistency.
+
+        Default action is to verify |ASN.1| object against constraints imposed
+        by `subtypeSpec`.
+
+        Raises
+        ------
+        :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found
+        """
+        if self.componentType is noValue or not self.subtypeSpec:
+            return False
+
+        if self._componentValues is noValue:
+            return True
+
+        mapping = {}
+
+        for idx, value in enumerate(self._componentValues):
+            # Absent fields are not in the mapping
+            if value is noValue:
+                continue
+
+            name = self.componentType.getNameByPosition(idx)
+
+            mapping[name] = value
+
+        try:
+            # Represent Sequence/Set as a bare dict to constraints chain
+            self.subtypeSpec(mapping)
+
+        except error.PyAsn1Error:
+            exc = sys.exc_info()[1]
+            return exc
+
+        return False
+
     def prettyPrint(self, scope=0):
         """Return an object representation string.
 
@@ -2717,10 +2774,6 @@
     #: 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 +2813,6 @@
     #: 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 +2933,9 @@
         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 +2975,7 @@
     #: 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)
     )
 
@@ -3004,7 +3048,7 @@
         if self._currentIdx is not None:
             yield self.componentType[self._currentIdx].getName(), self[self._currentIdx]
 
-    def verifySizeSpec(self):
+    def checkConsistency(self):
         if self._currentIdx is None:
             raise error.PyAsn1Error('Component not chosen')
 
@@ -3197,7 +3241,9 @@
         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/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py
index 089f0f3..e3b74df 100644
--- a/tests/codec/ber/test_decoder.py
+++ b/tests/codec/ber/test_decoder.py
@@ -486,17 +486,17 @@
 if sys.version_info[0:2] > (2, 5):
     class UniversalStringDecoderTestCase(BaseTestCase):
         def testDecoder(self):
-            assert decoder.decode(ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99))) == (char.UniversalString(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+            assert decoder.decode(ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99))) == (char.UniversalString(sys.version_info[0] >= 3 and 'abc' or unicode('abc')), null)
 
 
 class BMPStringDecoderTestCase(BaseTestCase):
     def testDecoder(self):
-        assert decoder.decode(ints2octs((30, 6, 0, 97, 0, 98, 0, 99))) == (char.BMPString(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+        assert decoder.decode(ints2octs((30, 6, 0, 97, 0, 98, 0, 99))) == (char.BMPString(sys.version_info[0] >= 3 and 'abc' or unicode('abc')), null)
 
 
 class UTF8StringDecoderTestCase(BaseTestCase):
     def testDecoder(self):
-        assert decoder.decode(ints2octs((12, 3, 97, 98, 99))) == (char.UTF8String(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+        assert decoder.decode(ints2octs((12, 3, 97, 98, 99))) == (char.UTF8String(sys.version_info[0] >= 3 and 'abc' or unicode('abc')), null)
 
 
 class SequenceOfDecoderTestCase(BaseTestCase):
diff --git a/tests/codec/ber/test_encoder.py b/tests/codec/ber/test_encoder.py
index 38d75c0..df82e7b 100644
--- a/tests/codec/ber/test_encoder.py
+++ b/tests/codec/ber/test_encoder.py
@@ -436,40 +436,40 @@
 if sys.version_info[0:2] > (2, 5):
     class UniversalStringEncoderTestCase(BaseTestCase):
         def testEncoding(self):
-            assert encoder.encode(char.UniversalString(sys.version_info[0] == 3 and 'abc' or unicode('abc'))) == ints2octs(
+            assert encoder.encode(char.UniversalString(sys.version_info[0] >= 3 and 'abc' or unicode('abc'))) == ints2octs(
                 (28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99)), 'Incorrect encoding'
 
 
     class UniversalStringEncoderWithSchemaTestCase(BaseTestCase):
         def testEncoding(self):
             assert encoder.encode(
-                sys.version_info[0] == 3 and 'abc' or unicode('abc'), asn1Spec=char.UniversalString()
+                sys.version_info[0] >= 3 and 'abc' or unicode('abc'), asn1Spec=char.UniversalString()
             ) == ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99)), 'Incorrect encoding'
 
 
 class BMPStringEncoderTestCase(BaseTestCase):
     def testEncoding(self):
-        assert encoder.encode(char.BMPString(sys.version_info[0] == 3 and 'abc' or unicode('abc'))) == ints2octs(
+        assert encoder.encode(char.BMPString(sys.version_info[0] >= 3 and 'abc' or unicode('abc'))) == ints2octs(
             (30, 6, 0, 97, 0, 98, 0, 99)), 'Incorrect encoding'
 
 
 class BMPStringEncoderWithSchemaTestCase(BaseTestCase):
     def testEncoding(self):
         assert encoder.encode(
-            sys.version_info[0] == 3 and 'abc' or unicode('abc'), asn1Spec=char.BMPString()
+            sys.version_info[0] >= 3 and 'abc' or unicode('abc'), asn1Spec=char.BMPString()
         ) == ints2octs((30, 6, 0, 97, 0, 98, 0, 99)), 'Incorrect encoding'
 
 
 class UTF8StringEncoderTestCase(BaseTestCase):
     def testEncoding(self):
-        assert encoder.encode(char.UTF8String(sys.version_info[0] == 3 and 'abc' or unicode('abc'))) == ints2octs(
+        assert encoder.encode(char.UTF8String(sys.version_info[0] >= 3 and 'abc' or unicode('abc'))) == ints2octs(
             (12, 3, 97, 98, 99)), 'Incorrect encoding'
 
 
 class UTF8StringEncoderWithSchemaTestCase(BaseTestCase):
     def testEncoding(self):
         assert encoder.encode(
-            sys.version_info[0] == 3 and 'abc' or unicode('abc'), asn1Spec=char.UTF8String()
+            sys.version_info[0] >= 3 and 'abc' or unicode('abc'), asn1Spec=char.UTF8String()
         ) == ints2octs((12, 3, 97, 98, 99)), 'Incorrect encoding'
 
 
diff --git a/tests/type/test_constraint.py b/tests/type/test_constraint.py
index b5276cd..0f49c78 100644
--- a/tests/type/test_constraint.py
+++ b/tests/type/test_constraint.py
@@ -128,6 +128,69 @@
             assert 0, 'constraint check fails'
 
 
+class WithComponentsConstraintTestCase(BaseTestCase):
+
+    def testGoodVal(self):
+        c = constraint.WithComponentsConstraint(
+            ('A', constraint.ComponentPresentConstraint()),
+            ('B', constraint.ComponentAbsentConstraint()))
+
+        try:
+            c({'A': 1})
+
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testGoodValWithExtraFields(self):
+        c = constraint.WithComponentsConstraint(
+            ('A', constraint.ComponentPresentConstraint()),
+            ('B', constraint.ComponentAbsentConstraint())
+        )
+
+        try:
+            c({'A': 1, 'C': 2})
+
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testEmptyConstraint(self):
+        c = constraint.WithComponentsConstraint()
+
+        try:
+            c({'A': 1})
+
+        except error.ValueConstraintError:
+            assert 0, 'constraint check fails'
+
+    def testBadVal(self):
+        c = constraint.WithComponentsConstraint(
+            ('A', constraint.ComponentPresentConstraint())
+        )
+
+        try:
+            c({'B': 2})
+
+        except error.ValueConstraintError:
+            pass
+
+        else:
+            assert 0, 'constraint check fails'
+
+    def testBadValExtraFields(self):
+        c = constraint.WithComponentsConstraint(
+            ('A', constraint.ComponentPresentConstraint())
+        )
+
+        try:
+            c({'B': 2, 'C': 3})
+
+        except error.ValueConstraintError:
+            pass
+
+        else:
+            assert 0, 'constraint check fails'
+
+
 class ConstraintsIntersectionTestCase(BaseTestCase):
     def setUp(self):
         BaseTestCase.setUp(self)
diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py
index 0092588..9762959 100644
--- a/tests/type/test_univ.py
+++ b/tests/type/test_univ.py
@@ -992,11 +992,13 @@
         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 @@
                 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'
 
@@ -1040,22 +1042,14 @@
         else:
             pass
 
-    def testSizeSpec(self):
-        s = self.s1.clone(sizeSpec=constraint.ConstraintsUnion(
+    def testConsistency(self):
+        s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion(
             constraint.ValueSizeConstraint(1, 1)
         ))
         s.setComponentByPosition(0, univ.OctetString('abc'))
-        try:
-            s.verifySizeSpec()
-        except PyAsn1Error:
-            assert 0, 'size spec fails'
+        assert not s.isInconsistent, 'size spec fails'
         s.setComponentByPosition(1, univ.OctetString('abc'))
-        try:
-            s.verifySizeSpec()
-        except PyAsn1Error:
-            pass
-        else:
-            assert 0, 'size spec fails'
+        assert s.isInconsistent, 'size spec fails'
 
     def testGetComponentTagMap(self):
         assert self.s1.componentType.tagMap.presentTypes == {
@@ -1065,15 +1059,13 @@
     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
@@ -1257,6 +1249,37 @@
 
         assert not s.isValue
 
+    def testIsInconsistentSizeConstraint(self):
+
+        class SequenceOf(univ.SequenceOf):
+            componentType = univ.OctetString()
+            subtypeSpec = constraint.ValueSizeConstraint(0, 1)
+
+        s = SequenceOf()
+
+        assert s.isInconsistent
+
+        s[0] = 'test'
+
+        assert not s.isInconsistent
+
+        s[0] = 'test'
+        s[1] = 'test'
+
+        assert s.isInconsistent
+
+        s.clear()
+
+        assert not s.isInconsistent
+
+        s.reset()
+
+        assert s.isInconsistent
+
+        s[1] = 'test'
+
+        assert not s.isInconsistent
+
 
 class SequenceOfPicklingTestCase(unittest.TestCase):
 
@@ -1593,6 +1616,77 @@
 
         assert not s.isValue
 
+    def testIsInconsistentWithComponentsConstraint(self):
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.OptionalNamedType('name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(65))
+            )
+            subtypeSpec = constraint.WithComponentsConstraint(
+                ('name', constraint.ComponentPresentConstraint()),
+                ('age', constraint.ComponentAbsentConstraint())
+            )
+
+        s = Sequence()
+
+        assert s.isInconsistent
+
+        s[0] = 'test'
+
+        assert not s.isInconsistent
+
+        s[0] = 'test'
+        s[1] = 23
+
+        assert s.isInconsistent
+
+        s.clear()
+
+        assert s.isInconsistent
+
+        s.reset()
+
+        assert s.isInconsistent
+
+        s[1] = 23
+
+        assert s.isInconsistent
+
+    def testIsInconsistentSizeConstraint(self):
+
+        class Sequence(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.OptionalNamedType('name', univ.OctetString()),
+                namedtype.DefaultedNamedType('age', univ.Integer(65))
+            )
+            subtypeSpec = constraint.ValueSizeConstraint(0, 1)
+
+        s = Sequence()
+
+        assert not s.isInconsistent
+
+        s[0] = 'test'
+
+        assert not s.isInconsistent
+
+        s[0] = 'test'
+        s[1] = 23
+
+        assert s.isInconsistent
+
+        s.clear()
+
+        assert not s.isInconsistent
+
+        s.reset()
+
+        assert s.isInconsistent
+
+        s[1] = 23
+
+        assert not s.isInconsistent
+
 
 class SequenceWithoutSchema(BaseTestCase):