summaryrefslogtreecommitdiff
path: root/pyasn1
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2019-08-27 10:17:42 +0200
committerGitHub <noreply@github.com>2019-08-27 10:17:42 +0200
commitd0b7f2ec8677eec8f9aa31103a66f5cab18e9308 (patch)
tree52c0e965e87f064d88a10bc43f9cfd9c86fd052a /pyasn1
parent7214dcae11f58b0c480bd30170da3eb4734ca707 (diff)
downloadpyasn1-git-d0b7f2ec8677eec8f9aa31103a66f5cab18e9308.tar.gz
Add `SET ... WITH COMPONENTS ...` ASN.1 construct support (#171)
Added `WithComponentsConstraint` along with related `ComponentPresentConstraint` and `ComponentAbsentConstraint` classes to be used with `Sequence`/`Set` types representing `SET ... WITH COMPONENTS ...` like ASN.1 constructs.
Diffstat (limited to 'pyasn1')
-rw-r--r--pyasn1/type/base.py18
-rw-r--r--pyasn1/type/constraint.py133
-rw-r--r--pyasn1/type/univ.py73
3 files changed, 206 insertions, 18 deletions
diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py
index 834b76e..994f1c9 100644
--- a/pyasn1/type/base.py
+++ b/pyasn1/type/base.py
@@ -677,24 +677,6 @@ class ConstructedAsn1Type(Asn1Type):
return clone
- @property
- def isInconsistent(self):
- """Run necessary checks to ensure 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
- """
- try:
- self.subtypeSpec(self)
-
- except error.PyAsn1Error:
- exc = sys.exc_info()[1]
- return exc
-
def getComponentByPosition(self, idx):
raise error.PyAsn1Error('Method not implemented')
diff --git a/pyasn1/type/constraint.py b/pyasn1/type/constraint.py
index 75db38a..b8aa0af 100644
--- a/pyasn1/type/constraint.py
+++ b/pyasn1/type/constraint.py
@@ -342,6 +342,139 @@ class PermittedAlphabetConstraint(SingleValueConstraint):
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.
+
+ 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 = ConstraintsIntersection(
+ WithComponentsConstraint(
+ ('id', ComponentPresentConstraint()),
+ ('name', ComponentAbsentConstraint())
+ ),
+ WithComponentsConstraint(
+ ('id', ComponentAbsentConstraint()),
+ ('name', ComponentPresentConstraint())
+ )
+ )
+
+ item = Item()
+
+ # This will succeed
+ item['id'] = 1
+
+ # This will succeed
+ item['name'] = 'John'
+
+ # This will fail on encoding
+ 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"""
diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py
index fbf8ed5..aa688b2 100644
--- a/pyasn1/type/univ.py
+++ b/pyasn1/type/univ.py
@@ -2042,6 +2042,41 @@ class SequenceOfAndSetOfBase(base.ConstructedAsn1Type):
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__
@@ -2637,6 +2672,44 @@ class SequenceAndSetBase(base.ConstructedAsn1Type):
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.