diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-10-22 15:56:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-22 15:56:51 +0200 |
commit | 9653bbb8b2eb9f1e6a6920d8239bd55b72f9550e (patch) | |
tree | de4527642fea0d264f367a577b0a526efe1d4416 /pyasn1 | |
parent | 262793a79cad797601fa855eec1e1020e1a534cb (diff) | |
download | pyasn1-git-9653bbb8b2eb9f1e6a6920d8239bd55b72f9550e.tar.gz |
docs on constraints added (#97)
Diffstat (limited to 'pyasn1')
-rw-r--r-- | pyasn1/type/constraint.py | 335 |
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 |