123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- #
- # This file is part of pyasn1 software.
- #
- # Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
- # License: http://snmplabs.com/pyasn1/license.html
- #
- # Original concept and code by Mike C. Fletcher.
- #
- import sys
-
- from pyasn1.type import error
-
- __all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint',
- 'ValueRangeConstraint', 'ValueSizeConstraint',
- 'PermittedAlphabetConstraint', 'InnerTypeConstraint',
- 'ConstraintsExclusion', 'ConstraintsIntersection',
- 'ConstraintsUnion']
-
-
- class AbstractConstraint(object):
-
- def __init__(self, *values):
- self._valueMap = set()
- self._setValues(values)
- self.__hash = hash((self.__class__.__name__, self._values))
-
- def __call__(self, value, idx=None):
- if not self._values:
- return
-
- try:
- self._testValue(value, idx)
-
- except error.ValueConstraintError:
- raise error.ValueConstraintError(
- '%s failed at: %r' % (self, sys.exc_info()[1])
- )
-
- def __repr__(self):
- representation = '%s object at 0x%x' % (self.__class__.__name__, id(self))
-
- if self._values:
- representation += ' consts %s' % ', '.join([repr(x) for x in self._values])
-
- return '<%s>' % representation
-
- def __eq__(self, other):
- return self is other and True or self._values == other
-
- def __ne__(self, other):
- return self._values != other
-
- def __lt__(self, other):
- return self._values < other
-
- def __le__(self, other):
- return self._values <= other
-
- def __gt__(self, other):
- return self._values > other
-
- def __ge__(self, other):
- return self._values >= other
-
- if sys.version_info[0] <= 2:
- def __nonzero__(self):
- return self._values and True or False
- else:
- def __bool__(self):
- return self._values and True or False
-
- def __hash__(self):
- return self.__hash
-
- def _setValues(self, values):
- self._values = values
-
- def _testValue(self, value, idx):
- raise error.ValueConstraintError(value)
-
- # Constraints derivation logic
- def getValueMap(self):
- return self._valueMap
-
- def isSuperTypeOf(self, otherConstraint):
- # TODO: fix possible comparison of set vs scalars here
- return (otherConstraint is self or
- not self._values or
- otherConstraint == self or
- self in otherConstraint.getValueMap())
-
- def isSubTypeOf(self, otherConstraint):
- return (otherConstraint is self or
- not self or
- otherConstraint == self or
- otherConstraint in self._valueMap)
-
-
- class SingleValueConstraint(AbstractConstraint):
- """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.
-
- Examples
- --------
- .. 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)
-
- def _testValue(self, value, idx):
- if value not in self._set:
- raise error.ValueConstraintError(value)
-
-
- class ContainedSubtypeConstraint(AbstractConstraint):
- """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.
-
- Examples
- --------
- .. 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 constraint in self._values:
- if isinstance(constraint, AbstractConstraint):
- constraint(value, idx)
- elif value not in self._set:
- raise error.ValueConstraintError(value)
-
-
- class ValueRangeConstraint(AbstractConstraint):
- """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)
-
- Examples
- --------
- .. 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)
-
- def _setValues(self, values):
- if len(values) != 2:
- raise error.PyAsn1Error(
- '%s: bad constraint values' % (self.__class__.__name__,)
- )
- self.start, self.stop = values
- if self.start > self.stop:
- raise error.PyAsn1Error(
- '%s: screwed constraint values (start > stop): %s > %s' % (
- self.__class__.__name__,
- self.start, self.stop
- )
- )
- AbstractConstraint._setValues(self, values)
-
-
- class ValueSizeConstraint(ValueRangeConstraint):
- """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)
-
- Examples
- --------
- .. 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:
- raise error.ValueConstraintError(value)
-
-
- 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.
-
- Examples
- --------
- .. 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)
-
- def _testValue(self, value, idx):
- if not self._set.issuperset(value):
- raise error.ValueConstraintError(value)
-
-
- # This is a bit kludgy, meaning two op modes within a single constraint
- class InnerTypeConstraint(AbstractConstraint):
- """Value must satisfy the type and presence constraints"""
-
- def _testValue(self, value, idx):
- if self.__singleTypeConstraint:
- self.__singleTypeConstraint(value)
- elif self.__multipleTypeConstraint:
- if idx not in self.__multipleTypeConstraint:
- raise error.ValueConstraintError(value)
- constraint, status = self.__multipleTypeConstraint[idx]
- if status == 'ABSENT': # XXX presense is not checked!
- raise error.ValueConstraintError(value)
- constraint(value)
-
- def _setValues(self, values):
- self.__multipleTypeConstraint = {}
- self.__singleTypeConstraint = None
- for v in values:
- if isinstance(v, tuple):
- self.__multipleTypeConstraint[v[0]] = v[1], v[2]
- else:
- self.__singleTypeConstraint = v
- AbstractConstraint._setValues(self, values)
-
-
- # Logic operations on constraints
-
- class ConstraintsExclusion(AbstractConstraint):
- """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.
-
- Examples
- --------
- .. 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)
- except error.ValueConstraintError:
- return
- else:
- raise error.ValueConstraintError(value)
-
- def _setValues(self, values):
- if len(values) != 1:
- raise error.PyAsn1Error('Single constraint expected')
-
- AbstractConstraint._setValues(self, values)
-
-
- class AbstractConstraintSet(AbstractConstraint):
-
- def __getitem__(self, idx):
- return self._values[idx]
-
- def __iter__(self):
- return iter(self._values)
-
- def __add__(self, value):
- return self.__class__(*(self._values + (value,)))
-
- def __radd__(self, value):
- return self.__class__(*((value,) + self._values))
-
- def __len__(self):
- return len(self._values)
-
- # Constraints inclusion in sets
-
- def _setValues(self, values):
- self._values = values
- for constraint in values:
- if constraint:
- self._valueMap.add(constraint)
- self._valueMap.update(constraint.getValueMap())
-
-
- class ConstraintsIntersection(AbstractConstraintSet):
- """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.
-
- Examples
- --------
- .. 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):
- """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.
-
- Examples
- --------
- .. 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:
- constraint(value, idx)
- except error.ValueConstraintError:
- pass
- else:
- return
-
- raise error.ValueConstraintError(
- 'all of %s failed for "%s"' % (self._values, value)
- )
-
- # TODO:
- # refactor InnerTypeConstraint
- # add tests for type check
- # implement other constraint types
- # make constraint validation easy to skip
|