Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyasn1/type/constraint.py: 54%
177 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:45 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:45 +0000
1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
5# License: https://pyasn1.readthedocs.io/en/latest/license.html
6#
7# Original concept and code by Mike C. Fletcher.
8#
9import sys
11from pyasn1.type import error
13__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint',
14 'ValueRangeConstraint', 'ValueSizeConstraint',
15 'PermittedAlphabetConstraint', 'InnerTypeConstraint',
16 'ConstraintsExclusion', 'ConstraintsIntersection',
17 'ConstraintsUnion']
20class AbstractConstraint(object):
22 def __init__(self, *values):
23 self._valueMap = set()
24 self._setValues(values)
25 self.__hash = hash((self.__class__.__name__, self._values))
27 def __call__(self, value, idx=None):
28 if not self._values:
29 return
31 try:
32 self._testValue(value, idx)
34 except error.ValueConstraintError:
35 raise error.ValueConstraintError(
36 '%s failed at: %r' % (self, sys.exc_info()[1])
37 )
39 def __repr__(self):
40 representation = '%s object' % (self.__class__.__name__)
42 if self._values:
43 representation += ', consts %s' % ', '.join(
44 [repr(x) for x in self._values])
46 return '<%s>' % representation
48 def __eq__(self, other):
49 return self is other and True or self._values == other
51 def __ne__(self, other):
52 return self._values != other
54 def __lt__(self, other):
55 return self._values < other
57 def __le__(self, other):
58 return self._values <= other
60 def __gt__(self, other):
61 return self._values > other
63 def __ge__(self, other):
64 return self._values >= other
66 if sys.version_info[0] <= 2:
67 def __nonzero__(self):
68 return self._values and True or False
69 else:
70 def __bool__(self):
71 return self._values and True or False
73 def __hash__(self):
74 return self.__hash
76 def _setValues(self, values):
77 self._values = values
79 def _testValue(self, value, idx):
80 raise error.ValueConstraintError(value)
82 # Constraints derivation logic
83 def getValueMap(self):
84 return self._valueMap
86 def isSuperTypeOf(self, otherConstraint):
87 # TODO: fix possible comparison of set vs scalars here
88 return (otherConstraint is self or
89 not self._values or
90 otherConstraint == self or
91 self in otherConstraint.getValueMap())
93 def isSubTypeOf(self, otherConstraint):
94 return (otherConstraint is self or
95 not self or
96 otherConstraint == self or
97 otherConstraint in self._valueMap)
100class SingleValueConstraint(AbstractConstraint):
101 """Create a SingleValueConstraint object.
103 The SingleValueConstraint satisfies any value that
104 is present in the set of permitted values.
106 Objects of this type are iterable (emitting constraint values) and
107 can act as operands for some arithmetic operations e.g. addition
108 and subtraction. The latter can be used for combining multiple
109 SingleValueConstraint objects into one.
111 The SingleValueConstraint object can be applied to
112 any ASN.1 type.
114 Parameters
115 ----------
116 *values: :class:`int`
117 Full set of values permitted by this constraint object.
119 Examples
120 --------
121 .. code-block:: python
123 class DivisorOfSix(Integer):
124 '''
125 ASN.1 specification:
127 Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6)
128 '''
129 subtypeSpec = SingleValueConstraint(1, 2, 3, 6)
131 # this will succeed
132 divisor_of_six = DivisorOfSix(1)
134 # this will raise ValueConstraintError
135 divisor_of_six = DivisorOfSix(7)
136 """
137 def _setValues(self, values):
138 self._values = values
139 self._set = set(values)
141 def _testValue(self, value, idx):
142 if value not in self._set:
143 raise error.ValueConstraintError(value)
145 # Constrains can be merged or reduced
147 def __contains__(self, item):
148 return item in self._set
150 def __iter__(self):
151 return iter(self._set)
153 def __sub__(self, constraint):
154 return self.__class__(*(self._set.difference(constraint)))
156 def __add__(self, constraint):
157 return self.__class__(*(self._set.union(constraint)))
159 def __sub__(self, constraint):
160 return self.__class__(*(self._set.difference(constraint)))
163class ContainedSubtypeConstraint(AbstractConstraint):
164 """Create a ContainedSubtypeConstraint object.
166 The ContainedSubtypeConstraint satisfies any value that
167 is present in the set of permitted values and also
168 satisfies included constraints.
170 The ContainedSubtypeConstraint object can be applied to
171 any ASN.1 type.
173 Parameters
174 ----------
175 *values:
176 Full set of values and constraint objects permitted
177 by this constraint object.
179 Examples
180 --------
181 .. code-block:: python
183 class DivisorOfEighteen(Integer):
184 '''
185 ASN.1 specification:
187 Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18)
188 '''
189 subtypeSpec = ContainedSubtypeConstraint(
190 SingleValueConstraint(1, 2, 3, 6), 9, 18
191 )
193 # this will succeed
194 divisor_of_eighteen = DivisorOfEighteen(9)
196 # this will raise ValueConstraintError
197 divisor_of_eighteen = DivisorOfEighteen(10)
198 """
199 def _testValue(self, value, idx):
200 for constraint in self._values:
201 if isinstance(constraint, AbstractConstraint):
202 constraint(value, idx)
203 elif value not in self._set:
204 raise error.ValueConstraintError(value)
207class ValueRangeConstraint(AbstractConstraint):
208 """Create a ValueRangeConstraint object.
210 The ValueRangeConstraint satisfies any value that
211 falls in the range of permitted values.
213 The ValueRangeConstraint object can only be applied
214 to :class:`~pyasn1.type.univ.Integer` and
215 :class:`~pyasn1.type.univ.Real` types.
217 Parameters
218 ----------
219 start: :class:`int`
220 Minimum permitted value in the range (inclusive)
222 end: :class:`int`
223 Maximum permitted value in the range (inclusive)
225 Examples
226 --------
227 .. code-block:: python
229 class TeenAgeYears(Integer):
230 '''
231 ASN.1 specification:
233 TeenAgeYears ::= INTEGER (13 .. 19)
234 '''
235 subtypeSpec = ValueRangeConstraint(13, 19)
237 # this will succeed
238 teen_year = TeenAgeYears(18)
240 # this will raise ValueConstraintError
241 teen_year = TeenAgeYears(20)
242 """
243 def _testValue(self, value, idx):
244 if value < self.start or value > self.stop:
245 raise error.ValueConstraintError(value)
247 def _setValues(self, values):
248 if len(values) != 2:
249 raise error.PyAsn1Error(
250 '%s: bad constraint values' % (self.__class__.__name__,)
251 )
252 self.start, self.stop = values
253 if self.start > self.stop:
254 raise error.PyAsn1Error(
255 '%s: screwed constraint values (start > stop): %s > %s' % (
256 self.__class__.__name__,
257 self.start, self.stop
258 )
259 )
260 AbstractConstraint._setValues(self, values)
263class ValueSizeConstraint(ValueRangeConstraint):
264 """Create a ValueSizeConstraint object.
266 The ValueSizeConstraint satisfies any value for
267 as long as its size falls within the range of
268 permitted sizes.
270 The ValueSizeConstraint object can be applied
271 to :class:`~pyasn1.type.univ.BitString`,
272 :class:`~pyasn1.type.univ.OctetString` (including
273 all :ref:`character ASN.1 types <type.char>`),
274 :class:`~pyasn1.type.univ.SequenceOf`
275 and :class:`~pyasn1.type.univ.SetOf` types.
277 Parameters
278 ----------
279 minimum: :class:`int`
280 Minimum permitted size of the value (inclusive)
282 maximum: :class:`int`
283 Maximum permitted size of the value (inclusive)
285 Examples
286 --------
287 .. code-block:: python
289 class BaseballTeamRoster(SetOf):
290 '''
291 ASN.1 specification:
293 BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames
294 '''
295 componentType = PlayerNames()
296 subtypeSpec = ValueSizeConstraint(1, 25)
298 # this will succeed
299 team = BaseballTeamRoster()
300 team.extend(['Jan', 'Matej'])
301 encode(team)
303 # this will raise ValueConstraintError
304 team = BaseballTeamRoster()
305 team.extend(['Jan'] * 26)
306 encode(team)
308 Note
309 ----
310 Whenever ValueSizeConstraint is applied to mutable types
311 (e.g. :class:`~pyasn1.type.univ.SequenceOf`,
312 :class:`~pyasn1.type.univ.SetOf`), constraint
313 validation only happens at the serialisation phase rather
314 than schema instantiation phase (as it is with immutable
315 types).
316 """
317 def _testValue(self, value, idx):
318 valueSize = len(value)
319 if valueSize < self.start or valueSize > self.stop:
320 raise error.ValueConstraintError(value)
323class PermittedAlphabetConstraint(SingleValueConstraint):
324 """Create a PermittedAlphabetConstraint object.
326 The PermittedAlphabetConstraint satisfies any character
327 string for as long as all its characters are present in
328 the set of permitted characters.
330 Objects of this type are iterable (emitting constraint values) and
331 can act as operands for some arithmetic operations e.g. addition
332 and subtraction.
334 The PermittedAlphabetConstraint object can only be applied
335 to the :ref:`character ASN.1 types <type.char>` such as
336 :class:`~pyasn1.type.char.IA5String`.
338 Parameters
339 ----------
340 *alphabet: :class:`str`
341 Full set of characters permitted by this constraint object.
343 Example
344 -------
345 .. code-block:: python
347 class BooleanValue(IA5String):
348 '''
349 ASN.1 specification:
351 BooleanValue ::= IA5String (FROM ('T' | 'F'))
352 '''
353 subtypeSpec = PermittedAlphabetConstraint('T', 'F')
355 # this will succeed
356 truth = BooleanValue('T')
357 truth = BooleanValue('TF')
359 # this will raise ValueConstraintError
360 garbage = BooleanValue('TAF')
362 ASN.1 `FROM ... EXCEPT ...` clause can be modelled by combining multiple
363 PermittedAlphabetConstraint objects into one:
365 Example
366 -------
367 .. code-block:: python
369 class Lipogramme(IA5String):
370 '''
371 ASN.1 specification:
373 Lipogramme ::=
374 IA5String (FROM (ALL EXCEPT ("e"|"E")))
375 '''
376 subtypeSpec = (
377 PermittedAlphabetConstraint(*string.printable) -
378 PermittedAlphabetConstraint('e', 'E')
379 )
381 # this will succeed
382 lipogramme = Lipogramme('A work of fiction?')
384 # this will raise ValueConstraintError
385 lipogramme = Lipogramme('Eel')
387 Note
388 ----
389 Although `ConstraintsExclusion` object could seemingly be used for this
390 purpose, practically, for it to work, it needs to represent its operand
391 constraints as sets and intersect one with the other. That would require
392 the insight into the constraint values (and their types) that are otherwise
393 hidden inside the constraint object.
395 Therefore it's more practical to model `EXCEPT` clause at
396 `PermittedAlphabetConstraint` level instead.
397 """
398 def _setValues(self, values):
399 self._values = values
400 self._set = set(values)
402 def _testValue(self, value, idx):
403 if not self._set.issuperset(value):
404 raise error.ValueConstraintError(value)
407class ComponentPresentConstraint(AbstractConstraint):
408 """Create a ComponentPresentConstraint object.
410 The ComponentPresentConstraint is only satisfied when the value
411 is not `None`.
413 The ComponentPresentConstraint object is typically used with
414 `WithComponentsConstraint`.
416 Examples
417 --------
418 .. code-block:: python
420 present = ComponentPresentConstraint()
422 # this will succeed
423 present('whatever')
425 # this will raise ValueConstraintError
426 present(None)
427 """
428 def _setValues(self, values):
429 self._values = ('<must be present>',)
431 if values:
432 raise error.PyAsn1Error('No arguments expected')
434 def _testValue(self, value, idx):
435 if value is None:
436 raise error.ValueConstraintError(
437 'Component is not present:')
440class ComponentAbsentConstraint(AbstractConstraint):
441 """Create a ComponentAbsentConstraint object.
443 The ComponentAbsentConstraint is only satisfied when the value
444 is `None`.
446 The ComponentAbsentConstraint object is typically used with
447 `WithComponentsConstraint`.
449 Examples
450 --------
451 .. code-block:: python
453 absent = ComponentAbsentConstraint()
455 # this will succeed
456 absent(None)
458 # this will raise ValueConstraintError
459 absent('whatever')
460 """
461 def _setValues(self, values):
462 self._values = ('<must be absent>',)
464 if values:
465 raise error.PyAsn1Error('No arguments expected')
467 def _testValue(self, value, idx):
468 if value is not None:
469 raise error.ValueConstraintError(
470 'Component is not absent: %r' % value)
473class WithComponentsConstraint(AbstractConstraint):
474 """Create a WithComponentsConstraint object.
476 The `WithComponentsConstraint` satisfies any mapping object that has
477 constrained fields present or absent, what is indicated by
478 `ComponentPresentConstraint` and `ComponentAbsentConstraint`
479 objects respectively.
481 The `WithComponentsConstraint` object is typically applied
482 to :class:`~pyasn1.type.univ.Set` or
483 :class:`~pyasn1.type.univ.Sequence` types.
485 Parameters
486 ----------
487 *fields: :class:`tuple`
488 Zero or more tuples of (`field`, `constraint`) indicating constrained
489 fields.
491 Notes
492 -----
493 On top of the primary use of `WithComponentsConstraint` (ensuring presence
494 or absence of particular components of a :class:`~pyasn1.type.univ.Set` or
495 :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other
496 constraint objects or their combinations. In case of scalar fields, these
497 constraints will be verified in addition to the constraints belonging to
498 scalar components themselves. However, formally, these additional
499 constraints do not change the type of these ASN.1 objects.
501 Examples
502 --------
504 .. code-block:: python
506 class Item(Sequence): # Set is similar
507 '''
508 ASN.1 specification:
510 Item ::= SEQUENCE {
511 id INTEGER OPTIONAL,
512 name OCTET STRING OPTIONAL
513 } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT
514 '''
515 componentType = NamedTypes(
516 OptionalNamedType('id', Integer()),
517 OptionalNamedType('name', OctetString())
518 )
519 withComponents = ConstraintsUnion(
520 WithComponentsConstraint(
521 ('id', ComponentPresentConstraint()),
522 ('name', ComponentAbsentConstraint())
523 ),
524 WithComponentsConstraint(
525 ('id', ComponentAbsentConstraint()),
526 ('name', ComponentPresentConstraint())
527 )
528 )
530 item = Item()
532 # This will succeed
533 item['id'] = 1
535 # This will succeed
536 item.reset()
537 item['name'] = 'John'
539 # This will fail (on encoding)
540 item.reset()
541 descr['id'] = 1
542 descr['name'] = 'John'
543 """
544 def _testValue(self, value, idx):
545 for field, constraint in self._values:
546 constraint(value.get(field))
548 def _setValues(self, values):
549 AbstractConstraint._setValues(self, values)
552# This is a bit kludgy, meaning two op modes within a single constraint
553class InnerTypeConstraint(AbstractConstraint):
554 """Value must satisfy the type and presence constraints"""
556 def _testValue(self, value, idx):
557 if self.__singleTypeConstraint:
558 self.__singleTypeConstraint(value)
559 elif self.__multipleTypeConstraint:
560 if idx not in self.__multipleTypeConstraint:
561 raise error.ValueConstraintError(value)
562 constraint, status = self.__multipleTypeConstraint[idx]
563 if status == 'ABSENT': # XXX presence is not checked!
564 raise error.ValueConstraintError(value)
565 constraint(value)
567 def _setValues(self, values):
568 self.__multipleTypeConstraint = {}
569 self.__singleTypeConstraint = None
570 for v in values:
571 if isinstance(v, tuple):
572 self.__multipleTypeConstraint[v[0]] = v[1], v[2]
573 else:
574 self.__singleTypeConstraint = v
575 AbstractConstraint._setValues(self, values)
578# Logic operations on constraints
580class ConstraintsExclusion(AbstractConstraint):
581 """Create a ConstraintsExclusion logic operator object.
583 The ConstraintsExclusion logic operator succeeds when the
584 value does *not* satisfy the operand constraint.
586 The ConstraintsExclusion object can be applied to
587 any constraint and logic operator object.
589 Parameters
590 ----------
591 *constraints:
592 Constraint or logic operator objects.
594 Examples
595 --------
596 .. code-block:: python
598 class LuckyNumber(Integer):
599 subtypeSpec = ConstraintsExclusion(
600 SingleValueConstraint(13)
601 )
603 # this will succeed
604 luckyNumber = LuckyNumber(12)
606 # this will raise ValueConstraintError
607 luckyNumber = LuckyNumber(13)
609 Note
610 ----
611 The `FROM ... EXCEPT ...` ASN.1 clause should be modeled by combining
612 constraint objects into one. See `PermittedAlphabetConstraint` for more
613 information.
614 """
615 def _testValue(self, value, idx):
616 for constraint in self._values:
617 try:
618 constraint(value, idx)
620 except error.ValueConstraintError:
621 continue
623 raise error.ValueConstraintError(value)
625 def _setValues(self, values):
626 AbstractConstraint._setValues(self, values)
629class AbstractConstraintSet(AbstractConstraint):
631 def __getitem__(self, idx):
632 return self._values[idx]
634 def __iter__(self):
635 return iter(self._values)
637 def __add__(self, value):
638 return self.__class__(*(self._values + (value,)))
640 def __radd__(self, value):
641 return self.__class__(*((value,) + self._values))
643 def __len__(self):
644 return len(self._values)
646 # Constraints inclusion in sets
648 def _setValues(self, values):
649 self._values = values
650 for constraint in values:
651 if constraint:
652 self._valueMap.add(constraint)
653 self._valueMap.update(constraint.getValueMap())
656class ConstraintsIntersection(AbstractConstraintSet):
657 """Create a ConstraintsIntersection logic operator object.
659 The ConstraintsIntersection logic operator only succeeds
660 if *all* its operands succeed.
662 The ConstraintsIntersection object can be applied to
663 any constraint and logic operator objects.
665 The ConstraintsIntersection object duck-types the immutable
666 container object like Python :py:class:`tuple`.
668 Parameters
669 ----------
670 *constraints:
671 Constraint or logic operator objects.
673 Examples
674 --------
675 .. code-block:: python
677 class CapitalAndSmall(IA5String):
678 '''
679 ASN.1 specification:
681 CapitalAndSmall ::=
682 IA5String (FROM ("A".."Z"|"a".."z"))
683 '''
684 subtypeSpec = ConstraintsIntersection(
685 PermittedAlphabetConstraint('A', 'Z'),
686 PermittedAlphabetConstraint('a', 'z')
687 )
689 # this will succeed
690 capital_and_small = CapitalAndSmall('Hello')
692 # this will raise ValueConstraintError
693 capital_and_small = CapitalAndSmall('hello')
694 """
695 def _testValue(self, value, idx):
696 for constraint in self._values:
697 constraint(value, idx)
700class ConstraintsUnion(AbstractConstraintSet):
701 """Create a ConstraintsUnion logic operator object.
703 The ConstraintsUnion logic operator succeeds if
704 *at least* a single operand succeeds.
706 The ConstraintsUnion object can be applied to
707 any constraint and logic operator objects.
709 The ConstraintsUnion object duck-types the immutable
710 container object like Python :py:class:`tuple`.
712 Parameters
713 ----------
714 *constraints:
715 Constraint or logic operator objects.
717 Examples
718 --------
719 .. code-block:: python
721 class CapitalOrSmall(IA5String):
722 '''
723 ASN.1 specification:
725 CapitalOrSmall ::=
726 IA5String (FROM ("A".."Z") | FROM ("a".."z"))
727 '''
728 subtypeSpec = ConstraintsUnion(
729 PermittedAlphabetConstraint('A', 'Z'),
730 PermittedAlphabetConstraint('a', 'z')
731 )
733 # this will succeed
734 capital_or_small = CapitalAndSmall('Hello')
736 # this will raise ValueConstraintError
737 capital_or_small = CapitalOrSmall('hello!')
738 """
739 def _testValue(self, value, idx):
740 for constraint in self._values:
741 try:
742 constraint(value, idx)
743 except error.ValueConstraintError:
744 pass
745 else:
746 return
748 raise error.ValueConstraintError(
749 'all of %s failed for "%s"' % (self._values, value)
750 )
752# TODO:
753# refactor InnerTypeConstraint
754# add tests for type check
755# implement other constraint types
756# make constraint validation easy to skip