summaryrefslogtreecommitdiff
path: root/pyasn1
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2017-10-22 15:56:51 +0200
committerGitHub <noreply@github.com>2017-10-22 15:56:51 +0200
commit9653bbb8b2eb9f1e6a6920d8239bd55b72f9550e (patch)
treede4527642fea0d264f367a577b0a526efe1d4416 /pyasn1
parent262793a79cad797601fa855eec1e1020e1a534cb (diff)
downloadpyasn1-git-9653bbb8b2eb9f1e6a6920d8239bd55b72f9550e.tar.gz
docs on constraints added (#97)
Diffstat (limited to 'pyasn1')
-rw-r--r--pyasn1/type/constraint.py335
1 files changed, 314 insertions, 21 deletions
diff --git a/pyasn1/type/constraint.py b/pyasn1/type/constraint.py
index 35bb0e2..c4f4238 100644
--- a/pyasn1/type/constraint.py
+++ b/pyasn1/type/constraint.py
@@ -15,12 +15,6 @@ __all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint', 'ValueRangeCon
class AbstractConstraint(object):
- """Abstract base-class for constraint objects
-
- Constraints should be stored in a simple sequence in the
- namespace of their client Asn1Item sub-classes in cases
- when ASN.1 constraint is define.
- """
def __init__(self, *values):
self._valueMap = set()
@@ -96,9 +90,39 @@ class AbstractConstraint(object):
otherConstraint == self or
otherConstraint in self._valueMap)
+
class SingleValueConstraint(AbstractConstraint):
- """Value must be part of defined values constraint"""
+ """Create a SingleValueConstraint object.
+
+ The SingleValueConstraint satisfies any value that
+ is present in the set of permitted values.
+
+ The SingleValueConstraint object can be applied to
+ any ASN.1 type.
+
+ Parameters
+ ----------
+ \*values: :class:`int`
+ Full set of values permitted by this constraint object.
+
+ Example
+ -------
+ .. code-block:: python
+
+ class DivisorOfSix(Integer):
+ '''
+ ASN.1 specification:
+
+ Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6)
+ '''
+ subtypeSpec = SingleValueConstraint(1, 2, 3, 6)
+
+ # this will succeed
+ divisor_of_six = DivisorOfSix(1)
+ # this will raise ValueConstraintError
+ divisor_of_six = DivisorOfSix(7)
+ """
def _setValues(self, values):
self._values = values
self._set = set(values)
@@ -109,16 +133,85 @@ class SingleValueConstraint(AbstractConstraint):
class ContainedSubtypeConstraint(AbstractConstraint):
- """Value must satisfy all of defined set of constraints"""
+ """Create a ContainedSubtypeConstraint object.
+
+ The ContainedSubtypeConstraint satisfies any value that
+ is present in the set of permitted values and also
+ satisfies included constraints.
+
+ The ContainedSubtypeConstraint object can be applied to
+ any ASN.1 type.
+
+ Parameters
+ ----------
+ \*values:
+ Full set of values and constraint objects permitted
+ by this constraint object.
+
+ Example
+ -------
+ .. code-block:: python
+
+ class DivisorOfEighteen(Integer):
+ '''
+ ASN.1 specification:
+
+ Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18)
+ '''
+ subtypeSpec = ContainedSubtypeConstraint(
+ SingleValueConstraint(1, 2, 3, 6), 9, 18
+ )
+
+ # this will succeed
+ divisor_of_eighteen = DivisorOfEighteen(9)
+ # this will raise ValueConstraintError
+ divisor_of_eighteen = DivisorOfEighteen(10)
+ """
def _testValue(self, value, idx):
- for c in self._values:
- c(value, idx)
+ for constraint in self._values:
+ if isinstance(constraint, AbstractConstraint):
+ constraint(value, idx)
+ elif value not in self._set:
+ raise error.ValueConstraintError(value)
class ValueRangeConstraint(AbstractConstraint):
- """Value must be within start and stop values (inclusive)"""
+ """Create a ValueRangeConstraint object.
+
+ The ValueRangeConstraint satisfies any value that
+ falls in the range of permitted values.
+
+ The ValueRangeConstraint object can only be applied
+ to :class:`~pyasn1.type.univ.Integer` and
+ :class:`~pyasn1.type.univ.Real` types.
+
+ Parameters
+ ----------
+ start: :class:`int`
+ Minimum permitted value in the range (inclusive)
+
+ end: :class:`int`
+ Maximum permitted value in the range (inclusive)
+
+ Example
+ -------
+ .. code-block:: python
+
+ class TeenAgeYears(Integer):
+ '''
+ ASN.1 specification:
+ TeenAgeYears ::= INTEGER (13 .. 19)
+ '''
+ subtypeSpec = ValueRangeConstraint(13, 19)
+
+ # this will succeed
+ teen_year = TeenAgeYears(18)
+
+ # this will raise ValueConstraintError
+ teen_year = TeenAgeYears(20)
+ """
def _testValue(self, value, idx):
if value < self.start or value > self.stop:
raise error.ValueConstraintError(value)
@@ -140,8 +233,59 @@ class ValueRangeConstraint(AbstractConstraint):
class ValueSizeConstraint(ValueRangeConstraint):
- """len(value) must be within start and stop values (inclusive)"""
-
+ """Create a ValueSizeConstraint object.
+
+ The ValueSizeConstraint satisfies any value for
+ as long as its size falls within the range of
+ permitted sizes.
+
+ The ValueSizeConstraint object can be applied
+ to :class:`~pyasn1.type.univ.BitString`,
+ :class:`~pyasn1.type.univ.OctetString` (including
+ all :ref:`character ASN.1 types <type.char>`),
+ :class:`~pyasn1.type.univ.SequenceOf`
+ and :class:`~pyasn1.type.univ.SetOf` types.
+
+ Parameters
+ ----------
+ minimum: :class:`int`
+ Minimum permitted size of the value (inclusive)
+
+ maximum: :class:`int`
+ Maximum permitted size of the value (inclusive)
+
+ Example
+ -------
+ .. code-block:: python
+
+ class BaseballTeamRoster(SetOf):
+ '''
+ ASN.1 specification:
+
+ BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames
+ '''
+ componentType = PlayerNames()
+ subtypeSpec = ValueSizeConstraint(1, 25)
+
+ # this will succeed
+ team = BaseballTeamRoster()
+ team.extend(['Jan', 'Matej'])
+ encode(team)
+
+ # this will raise ValueConstraintError
+ team = BaseballTeamRoster()
+ team.extend(['Jan'] * 26)
+ encode(team)
+
+ Note
+ ----
+ Whenever ValueSizeConstraint is applied to mutable types
+ (e.g. :class:`~pyasn1.type.univ.SequenceOf`,
+ :class:`~pyasn1.type.univ.SetOf`), constraint
+ validation only happens at the serialisation phase rather
+ than schema instantiation phase (as it is with immutable
+ types).
+ """
def _testValue(self, value, idx):
valueSize = len(value)
if valueSize < self.start or valueSize > self.stop:
@@ -149,6 +293,40 @@ class ValueSizeConstraint(ValueRangeConstraint):
class PermittedAlphabetConstraint(SingleValueConstraint):
+ """Create a PermittedAlphabetConstraint object.
+
+ The PermittedAlphabetConstraint satisfies any character
+ string for as long as all its characters are present in
+ the set of permitted characters.
+
+ The PermittedAlphabetConstraint object can only be applied
+ to the :ref:`character ASN.1 types <type.char>` such as
+ :class:`~pyasn1.type.char.IA5String`.
+
+ Parameters
+ ----------
+ \*alphabet: :class:`str`
+ Full set of characters permitted by this constraint object.
+
+ Example
+ -------
+ .. code-block:: python
+
+ class BooleanValue(IA5String):
+ '''
+ ASN.1 specification:
+
+ BooleanValue ::= IA5String (FROM ('T' | 'F'))
+ '''
+ subtypeSpec = PermittedAlphabetConstraint('T', 'F')
+
+ # this will succeed
+ truth = BooleanValue('T')
+ truth = BooleanValue('TF')
+
+ # this will raise ValueConstraintError
+ garbage = BooleanValue('TAF')
+ """
def _setValues(self, values):
self._values = values
self._set = set(values)
@@ -160,7 +338,7 @@ class PermittedAlphabetConstraint(SingleValueConstraint):
# This is a bit kludgy, meaning two op modes within a single constraint
class InnerTypeConstraint(AbstractConstraint):
- """Value must satisfy type and presense constraints"""
+ """Value must satisfy the type and presence constraints"""
def _testValue(self, value, idx):
if self.__singleTypeConstraint:
@@ -184,11 +362,50 @@ class InnerTypeConstraint(AbstractConstraint):
AbstractConstraint._setValues(self, values)
-# Boolean ops on constraints
+# Logic operations on constraints
class ConstraintsExclusion(AbstractConstraint):
- """Value must not fit the single constraint"""
+ """Create a ConstraintsExclusion logic operator object.
+
+ The ConstraintsExclusion logic operator succeeds when the
+ value does *not* satisfy the operand constraint.
+
+ The ConstraintsExclusion object can be applied to
+ any constraint and logic operator object.
+
+ Parameters
+ ----------
+ constraint:
+ Constraint or logic operator object.
+
+ Example
+ -------
+ .. code-block:: python
+
+ class Lipogramme(IA5STRING):
+ '''
+ ASN.1 specification:
+ Lipogramme ::=
+ IA5String (FROM (ALL EXCEPT ("e"|"E")))
+ '''
+ subtypeSpec = ConstraintsExclusion(
+ PermittedAlphabetConstraint('e', 'E')
+ )
+
+ # this will succeed
+ lipogramme = Lipogramme('A work of fiction?')
+
+ # this will raise ValueConstraintError
+ lipogramme = Lipogramme('Eel')
+
+ Warning
+ -------
+ The above example involving PermittedAlphabetConstraint might
+ not work due to the way how PermittedAlphabetConstraint works.
+ The other constraints might work with ConstraintsExclusion
+ though.
+ """
def _testValue(self, value, idx):
try:
self._values[0](value, idx)
@@ -200,11 +417,11 @@ class ConstraintsExclusion(AbstractConstraint):
def _setValues(self, values):
if len(values) != 1:
raise error.PyAsn1Error('Single constraint expected')
+
AbstractConstraint._setValues(self, values)
class AbstractConstraintSet(AbstractConstraint):
- """Value must not satisfy the single constraint"""
def __getitem__(self, idx):
return self._values[idx]
@@ -232,16 +449,88 @@ class AbstractConstraintSet(AbstractConstraint):
class ConstraintsIntersection(AbstractConstraintSet):
- """Value must satisfy all constraints"""
+ """Create a ConstraintsIntersection logic operator object.
+
+ The ConstraintsIntersection logic operator only succeeds
+ if *all* its operands succeed.
+
+ The ConstraintsIntersection object can be applied to
+ any constraint and logic operator objects.
+
+ The ConstraintsIntersection object duck-types the immutable
+ container object like Python :py:class:`tuple`.
+ Parameters
+ ----------
+ \*constraints:
+ Constraint or logic operator objects.
+
+ Example
+ -------
+ .. code-block:: python
+
+ class CapitalAndSmall(IA5String):
+ '''
+ ASN.1 specification:
+
+ CapitalAndSmall ::=
+ IA5String (FROM ("A".."Z"|"a".."z"))
+ '''
+ subtypeSpec = ConstraintsIntersection(
+ PermittedAlphabetConstraint('A', 'Z'),
+ PermittedAlphabetConstraint('a', 'z')
+ )
+
+ # this will succeed
+ capital_and_small = CapitalAndSmall('Hello')
+
+ # this will raise ValueConstraintError
+ capital_and_small = CapitalAndSmall('hello')
+ """
def _testValue(self, value, idx):
for constraint in self._values:
constraint(value, idx)
class ConstraintsUnion(AbstractConstraintSet):
- """Value must satisfy at least one constraint"""
+ """Create a ConstraintsUnion logic operator object.
+
+ The ConstraintsUnion logic operator only succeeds if
+ *at least a single* operand succeeds.
+
+ The ConstraintsUnion object can be applied to
+ any constraint and logic operator objects.
+ The ConstraintsUnion object duck-types the immutable
+ container object like Python :py:class:`tuple`.
+
+ Parameters
+ ----------
+ \*constraints:
+ Constraint or logic operator objects.
+
+ Example
+ -------
+ .. code-block:: python
+
+ class CapitalOrSmall(IA5String):
+ '''
+ ASN.1 specification:
+
+ CapitalOrSmall ::=
+ IA5String (FROM ("A".."Z") | FROM ("a".."z"))
+ '''
+ subtypeSpec = ConstraintsIntersection(
+ PermittedAlphabetConstraint('A', 'Z'),
+ PermittedAlphabetConstraint('a', 'z')
+ )
+
+ # this will succeed
+ capital_or_small = CapitalAndSmall('Hello')
+
+ # this will raise ValueConstraintError
+ capital_or_small = CapitalOrSmall('hello!')
+ """
def _testValue(self, value, idx):
for constraint in self._values:
try:
@@ -250,9 +539,13 @@ class ConstraintsUnion(AbstractConstraintSet):
pass
else:
return
+
raise error.ValueConstraintError(
- 'all of %s failed for \"%s\"' % (self._values, value)
+ 'all of %s failed for "%s"' % (self._values, value)
)
-# XXX
+# TODO:
+# refactor InnerTypeConstraint
# add tests for type check
+# implement other constraint types
+# make constraint validation easy to skip