| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- <html>
- <title>
- PyASN1 subtype constraints
- </title>
- <head>
- </head>
- <body>
- <center>
- <table width=60%>
- <tr>
- <td>
- <h4>
- 1.4 PyASN1 subtype constraints
- </h4>
- <p>
- Most ASN.1 types can correspond to an infinite set of values. To adapt to
- particular application's data model and needs, ASN.1 provides a mechanism
- for limiting the infinite set to values, that make sense in particular case.
- </p>
- <p>
- Imposing value constraints on an ASN.1 type can also be seen as creating
- a subtype from its base type.
- </p>
- <p>
- In pyasn1, constraints take shape of immutable objects capable
- of evaluating given value against constraint-specific requirements.
- Constraint object is a property of pyasn1 type. Like TagSet property,
- associated with every pyasn1 type, constraints can never be modified
- in place. The only way to modify pyasn1 type constraint is to associate
- new constraint object to a new pyasn1 type object.
- </p>
- <p>
- A handful of different flavors of <i>constraints</i> are defined in ASN.1.
- We will discuss them one by one in the following chapters and also explain
- how to combine and apply them to types.
- </p>
- <a name="1.4.1"></a>
- <h4>
- 1.4.1 Single value constraint
- </h4>
- <p>
- This kind of constraint allows for limiting type to a finite, specified set
- of values.
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- DialButton ::= OCTET STRING (
- "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
- )
- </pre>
- </td></tr></table>
- <p>
- Its pyasn1 implementation would look like:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import constraint
- >>> c = constraint.SingleValueConstraint(
- '0','1','2','3','4','5','6','7','8','9'
- )
- >>> c
- SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
- >>> c('0')
- >>> c('A')
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
- >>>
- </pre>
- </td></tr></table>
- <p>
- As can be seen in the snippet above, if a value violates the constraint, an
- exception will be thrown. A constrainted pyasn1 type object holds a
- reference to a constraint object (or their combination, as will be explained
- later) and calls it for value verification.
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import univ, constraint
- >>> class DialButton(univ.OctetString):
- ... subtypeSpec = constraint.SingleValueConstraint(
- ... '0','1','2','3','4','5','6','7','8','9'
- ... )
- >>> DialButton('0')
- DialButton(b'0')
- >>> DialButton('A')
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
- >>>
- </pre>
- </td></tr></table>
- <p>
- Constrained pyasn1 value object can never hold a violating value.
- </p>
- <a name="1.4.2"></a>
- <h4>
- 1.4.2 Value range constraint
- </h4>
- <p>
- A pair of values, compliant to a type to be constrained, denote low and upper
- bounds of allowed range of values of a type.
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- Teenagers ::= INTEGER (13..19)
- </pre>
- </td></tr></table>
- <p>
- And in pyasn1 terms:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import univ, constraint
- >>> class Teenagers(univ.Integer):
- ... subtypeSpec = constraint.ValueRangeConstraint(13, 19)
- >>> Teenagers(14)
- Teenagers(14)
- >>> Teenagers(20)
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- ValueRangeConstraint(13, 19) failed at: 20
- >>>
- </pre>
- </td></tr></table>
- <p>
- Value range constraint usually applies numeric types.
- </p>
- <a name="1.4.3"></a>
- <h4>
- 1.4.3 Size constraint
- </h4>
- <p>
- It is sometimes convenient to set or limit the allowed size of a data item
- to be sent from one application to another to manage bandwidth and memory
- consumption issues. Size constraint specifies the lower and upper bounds
- of the size of a valid value.
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- TwoBits ::= BIT STRING (SIZE (2))
- </pre>
- </td></tr></table>
- <p>
- Express the same grammar in pyasn1:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import univ, constraint
- >>> class TwoBits(univ.BitString):
- ... subtypeSpec = constraint.ValueSizeConstraint(2, 2)
- >>> TwoBits((1,1))
- TwoBits("'11'B")
- >>> TwoBits((1,1,0))
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- ValueSizeConstraint(2, 2) failed at: (1, 1, 0)
- >>>
- </pre>
- </td></tr></table>
- <p>
- Size constraint can be applied to potentially massive values - bit or octet
- strings, SEQUENCE OF/SET OF values.
- </p>
- <a name="1.4.4"></a>
- <h4>
- 1.4.4 Alphabet constraint
- </h4>
- <p>
- The permitted alphabet constraint is similar to Single value constraint
- but constraint applies to individual characters of a value.
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- MorseCode ::= PrintableString (FROM ("."|"-"|" "))
- </pre>
- </td></tr></table>
- <p>
- And in pyasn1:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import char, constraint
- >>> class MorseCode(char.PrintableString):
- ... subtypeSpec = constraint.PermittedAlphabetConstraint(".", "-", " ")
- >>> MorseCode("...---...")
- MorseCode('...---...')
- >>> MorseCode("?")
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- PermittedAlphabetConstraint(".", "-", " ") failed at: "?"
- >>>
- </pre>
- </td></tr></table>
- <p>
- Current implementation does not handle ranges of characters in constraint
- (FROM "A".."Z" syntax), one has to list the whole set in a range.
- </p>
- <a name="1.4.5"></a>
- <h4>
- 1.4.5 Constraint combinations
- </h4>
- <p>
- Up to this moment, we used a single constraint per ASN.1 type. The standard,
- however, allows for combining multiple individual constraints into
- intersections, unions and exclusions.
- </p>
- <p>
- In pyasn1 data model, all of these methods of constraint combinations are
- implemented as constraint-like objects holding individual constraint (or
- combination) objects. Like terminal constraint objects, combination objects
- are capable to perform value verification at its set of enclosed constraints
- according to the logic of particular combination.
- </p>
- <p>
- Constraints intersection verification succeeds only if a value is
- compliant to each constraint in a set. To begin with, the following
- specification will constitute a valid telephone number:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- PhoneNumber ::= NumericString (FROM ("0".."9")) (SIZE 11)
- </pre>
- </td></tr></table>
- <p>
- Constraint intersection object serves the logic above:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import char, constraint
- >>> class PhoneNumber(char.NumericString):
- ... subtypeSpec = constraint.ConstraintsIntersection(
- ... constraint.PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
- ... constraint.ValueSizeConstraint(11, 11)
- ... )
- >>> PhoneNumber('79039343212')
- PhoneNumber('79039343212')
- >>> PhoneNumber('?9039343212')
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- ConstraintsIntersection(
- PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
- ValueSizeConstraint(11, 11)) failed at:
- PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212"
- >>> PhoneNumber('9343212')
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- ConstraintsIntersection(
- PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
- ValueSizeConstraint(11, 11)) failed at:
- ValueSizeConstraint(10, 10) failed at: "9343212"
- >>>
- </pre>
- </td></tr></table>
- <p>
- Union of constraints works by making sure that a value is compliant
- to any of the constraint in a set. For instance:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- CapitalOrSmall ::= IA5String (FROM ('A','B','C') | FROM ('a','b','c'))
- </pre>
- </td></tr></table>
- <p>
- It's important to note, that a value must fully comply to any single
- constraint in a set. In the specification above, a value of all small or
- all capital letters is compliant, but a mix of small&capitals is not.
- Here's its pyasn1 analogue:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import char, constraint
- >>> class CapitalOrSmall(char.IA5String):
- ... subtypeSpec = constraint.ConstraintsUnion(
- ... constraint.PermittedAlphabetConstraint('A','B','C'),
- ... constraint.PermittedAlphabetConstraint('a','b','c')
- ... )
- >>> CapitalOrSmall('ABBA')
- CapitalOrSmall('ABBA')
- >>> CapitalOrSmall('abba')
- CapitalOrSmall('abba')
- >>> CapitalOrSmall('Abba')
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'),
- PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba"
- >>>
- </pre>
- </td></tr></table>
- <p>
- Finally, the exclusion constraint simply negates the logic of value
- verification at a constraint. In the following example, any integer value
- is allowed in a type but not zero.
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- NoZero ::= INTEGER (ALL EXCEPT 0)
- </pre>
- </td></tr></table>
- <p>
- In pyasn1 the above definition would read:
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import univ, constraint
- >>> class NoZero(univ.Integer):
- ... subtypeSpec = constraint.ConstraintsExclusion(
- ... constraint.SingleValueConstraint(0)
- ... )
- >>> NoZero(1)
- NoZero(1)
- >>> NoZero(0)
- Traceback (most recent call last):
- ...
- pyasn1.type.error.ValueConstraintError:
- ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0
- >>>
- </pre>
- </td></tr></table>
- <p>
- The depth of such a constraints tree, built with constraint combination objects
- at its nodes, has not explicit limit. Value verification is performed in a
- recursive manner till a definite solution is found.
- </p>
- <a name="1.5"></a>
- <h4>
- 1.5 Types relationships
- </h4>
- <p>
- In the course of data processing in an application, it is sometimes
- convenient to figure out the type relationships between pyasn1 type or
- value objects. Formally, two things influence pyasn1 types relationship:
- <i>tag set</i> and <i>subtype constraints</i>. One pyasn1 type is considered
- to be a derivative of another if their TagSet and Constraint objects are
- a derivation of one another.
- </p>
- <p>
- The following example illustrates the concept (we use the same tagset but
- different constraints for simplicity):
- </p>
- <table bgcolor="lightgray" border=0 width=100%><TR><TD>
- <pre>
- >>> from pyasn1.type import univ, constraint
- >>> i1 = univ.Integer(subtypeSpec=constraint.ValueRangeConstraint(3,8))
- >>> i2 = univ.Integer(subtypeSpec=constraint.ConstraintsIntersection(
- ... constraint.ValueRangeConstraint(3,8),
- ... constraint.ValueRangeConstraint(4,7)
- ... ) )
- >>> i1.isSameTypeWith(i2)
- False
- >>> i1.isSuperTypeOf(i2)
- True
- >>> i1.isSuperTypeOf(i1)
- True
- >>> i2.isSuperTypeOf(i1)
- False
- >>>
- </pre>
- </td></tr></table>
- <p>
- As can be seen in the above code snippet, there are two methods of any pyasn1
- type/value object that test types for their relationship:
- <b>isSameTypeWith</b>() and <b>isSuperTypeOf</b>(). The former is
- self-descriptive while the latter yields true if the argument appears
- to be a pyasn1 object which has tagset and constraints derived from those
- of the object being called.
- </p>
- <hr>
- </td>
- </tr>
- </table>
- </center>
- </body>
- </html>
|