Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

constraint.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. #
  2. # This file is part of pyasn1 software.
  3. #
  4. # Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
  5. # License: http://snmplabs.com/pyasn1/license.html
  6. #
  7. # Original concept and code by Mike C. Fletcher.
  8. #
  9. import sys
  10. from pyasn1.type import error
  11. __all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint',
  12. 'ValueRangeConstraint', 'ValueSizeConstraint',
  13. 'PermittedAlphabetConstraint', 'InnerTypeConstraint',
  14. 'ConstraintsExclusion', 'ConstraintsIntersection',
  15. 'ConstraintsUnion']
  16. class AbstractConstraint(object):
  17. def __init__(self, *values):
  18. self._valueMap = set()
  19. self._setValues(values)
  20. self.__hash = hash((self.__class__.__name__, self._values))
  21. def __call__(self, value, idx=None):
  22. if not self._values:
  23. return
  24. try:
  25. self._testValue(value, idx)
  26. except error.ValueConstraintError:
  27. raise error.ValueConstraintError(
  28. '%s failed at: %r' % (self, sys.exc_info()[1])
  29. )
  30. def __repr__(self):
  31. representation = '%s object at 0x%x' % (self.__class__.__name__, id(self))
  32. if self._values:
  33. representation += ' consts %s' % ', '.join([repr(x) for x in self._values])
  34. return '<%s>' % representation
  35. def __eq__(self, other):
  36. return self is other and True or self._values == other
  37. def __ne__(self, other):
  38. return self._values != other
  39. def __lt__(self, other):
  40. return self._values < other
  41. def __le__(self, other):
  42. return self._values <= other
  43. def __gt__(self, other):
  44. return self._values > other
  45. def __ge__(self, other):
  46. return self._values >= other
  47. if sys.version_info[0] <= 2:
  48. def __nonzero__(self):
  49. return self._values and True or False
  50. else:
  51. def __bool__(self):
  52. return self._values and True or False
  53. def __hash__(self):
  54. return self.__hash
  55. def _setValues(self, values):
  56. self._values = values
  57. def _testValue(self, value, idx):
  58. raise error.ValueConstraintError(value)
  59. # Constraints derivation logic
  60. def getValueMap(self):
  61. return self._valueMap
  62. def isSuperTypeOf(self, otherConstraint):
  63. # TODO: fix possible comparison of set vs scalars here
  64. return (otherConstraint is self or
  65. not self._values or
  66. otherConstraint == self or
  67. self in otherConstraint.getValueMap())
  68. def isSubTypeOf(self, otherConstraint):
  69. return (otherConstraint is self or
  70. not self or
  71. otherConstraint == self or
  72. otherConstraint in self._valueMap)
  73. class SingleValueConstraint(AbstractConstraint):
  74. """Create a SingleValueConstraint object.
  75. The SingleValueConstraint satisfies any value that
  76. is present in the set of permitted values.
  77. The SingleValueConstraint object can be applied to
  78. any ASN.1 type.
  79. Parameters
  80. ----------
  81. \*values: :class:`int`
  82. Full set of values permitted by this constraint object.
  83. Examples
  84. --------
  85. .. code-block:: python
  86. class DivisorOfSix(Integer):
  87. '''
  88. ASN.1 specification:
  89. Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6)
  90. '''
  91. subtypeSpec = SingleValueConstraint(1, 2, 3, 6)
  92. # this will succeed
  93. divisor_of_six = DivisorOfSix(1)
  94. # this will raise ValueConstraintError
  95. divisor_of_six = DivisorOfSix(7)
  96. """
  97. def _setValues(self, values):
  98. self._values = values
  99. self._set = set(values)
  100. def _testValue(self, value, idx):
  101. if value not in self._set:
  102. raise error.ValueConstraintError(value)
  103. class ContainedSubtypeConstraint(AbstractConstraint):
  104. """Create a ContainedSubtypeConstraint object.
  105. The ContainedSubtypeConstraint satisfies any value that
  106. is present in the set of permitted values and also
  107. satisfies included constraints.
  108. The ContainedSubtypeConstraint object can be applied to
  109. any ASN.1 type.
  110. Parameters
  111. ----------
  112. \*values:
  113. Full set of values and constraint objects permitted
  114. by this constraint object.
  115. Examples
  116. --------
  117. .. code-block:: python
  118. class DivisorOfEighteen(Integer):
  119. '''
  120. ASN.1 specification:
  121. Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18)
  122. '''
  123. subtypeSpec = ContainedSubtypeConstraint(
  124. SingleValueConstraint(1, 2, 3, 6), 9, 18
  125. )
  126. # this will succeed
  127. divisor_of_eighteen = DivisorOfEighteen(9)
  128. # this will raise ValueConstraintError
  129. divisor_of_eighteen = DivisorOfEighteen(10)
  130. """
  131. def _testValue(self, value, idx):
  132. for constraint in self._values:
  133. if isinstance(constraint, AbstractConstraint):
  134. constraint(value, idx)
  135. elif value not in self._set:
  136. raise error.ValueConstraintError(value)
  137. class ValueRangeConstraint(AbstractConstraint):
  138. """Create a ValueRangeConstraint object.
  139. The ValueRangeConstraint satisfies any value that
  140. falls in the range of permitted values.
  141. The ValueRangeConstraint object can only be applied
  142. to :class:`~pyasn1.type.univ.Integer` and
  143. :class:`~pyasn1.type.univ.Real` types.
  144. Parameters
  145. ----------
  146. start: :class:`int`
  147. Minimum permitted value in the range (inclusive)
  148. end: :class:`int`
  149. Maximum permitted value in the range (inclusive)
  150. Examples
  151. --------
  152. .. code-block:: python
  153. class TeenAgeYears(Integer):
  154. '''
  155. ASN.1 specification:
  156. TeenAgeYears ::= INTEGER (13 .. 19)
  157. '''
  158. subtypeSpec = ValueRangeConstraint(13, 19)
  159. # this will succeed
  160. teen_year = TeenAgeYears(18)
  161. # this will raise ValueConstraintError
  162. teen_year = TeenAgeYears(20)
  163. """
  164. def _testValue(self, value, idx):
  165. if value < self.start or value > self.stop:
  166. raise error.ValueConstraintError(value)
  167. def _setValues(self, values):
  168. if len(values) != 2:
  169. raise error.PyAsn1Error(
  170. '%s: bad constraint values' % (self.__class__.__name__,)
  171. )
  172. self.start, self.stop = values
  173. if self.start > self.stop:
  174. raise error.PyAsn1Error(
  175. '%s: screwed constraint values (start > stop): %s > %s' % (
  176. self.__class__.__name__,
  177. self.start, self.stop
  178. )
  179. )
  180. AbstractConstraint._setValues(self, values)
  181. class ValueSizeConstraint(ValueRangeConstraint):
  182. """Create a ValueSizeConstraint object.
  183. The ValueSizeConstraint satisfies any value for
  184. as long as its size falls within the range of
  185. permitted sizes.
  186. The ValueSizeConstraint object can be applied
  187. to :class:`~pyasn1.type.univ.BitString`,
  188. :class:`~pyasn1.type.univ.OctetString` (including
  189. all :ref:`character ASN.1 types <type.char>`),
  190. :class:`~pyasn1.type.univ.SequenceOf`
  191. and :class:`~pyasn1.type.univ.SetOf` types.
  192. Parameters
  193. ----------
  194. minimum: :class:`int`
  195. Minimum permitted size of the value (inclusive)
  196. maximum: :class:`int`
  197. Maximum permitted size of the value (inclusive)
  198. Examples
  199. --------
  200. .. code-block:: python
  201. class BaseballTeamRoster(SetOf):
  202. '''
  203. ASN.1 specification:
  204. BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames
  205. '''
  206. componentType = PlayerNames()
  207. subtypeSpec = ValueSizeConstraint(1, 25)
  208. # this will succeed
  209. team = BaseballTeamRoster()
  210. team.extend(['Jan', 'Matej'])
  211. encode(team)
  212. # this will raise ValueConstraintError
  213. team = BaseballTeamRoster()
  214. team.extend(['Jan'] * 26)
  215. encode(team)
  216. Note
  217. ----
  218. Whenever ValueSizeConstraint is applied to mutable types
  219. (e.g. :class:`~pyasn1.type.univ.SequenceOf`,
  220. :class:`~pyasn1.type.univ.SetOf`), constraint
  221. validation only happens at the serialisation phase rather
  222. than schema instantiation phase (as it is with immutable
  223. types).
  224. """
  225. def _testValue(self, value, idx):
  226. valueSize = len(value)
  227. if valueSize < self.start or valueSize > self.stop:
  228. raise error.ValueConstraintError(value)
  229. class PermittedAlphabetConstraint(SingleValueConstraint):
  230. """Create a PermittedAlphabetConstraint object.
  231. The PermittedAlphabetConstraint satisfies any character
  232. string for as long as all its characters are present in
  233. the set of permitted characters.
  234. The PermittedAlphabetConstraint object can only be applied
  235. to the :ref:`character ASN.1 types <type.char>` such as
  236. :class:`~pyasn1.type.char.IA5String`.
  237. Parameters
  238. ----------
  239. \*alphabet: :class:`str`
  240. Full set of characters permitted by this constraint object.
  241. Examples
  242. --------
  243. .. code-block:: python
  244. class BooleanValue(IA5String):
  245. '''
  246. ASN.1 specification:
  247. BooleanValue ::= IA5String (FROM ('T' | 'F'))
  248. '''
  249. subtypeSpec = PermittedAlphabetConstraint('T', 'F')
  250. # this will succeed
  251. truth = BooleanValue('T')
  252. truth = BooleanValue('TF')
  253. # this will raise ValueConstraintError
  254. garbage = BooleanValue('TAF')
  255. """
  256. def _setValues(self, values):
  257. self._values = values
  258. self._set = set(values)
  259. def _testValue(self, value, idx):
  260. if not self._set.issuperset(value):
  261. raise error.ValueConstraintError(value)
  262. # This is a bit kludgy, meaning two op modes within a single constraint
  263. class InnerTypeConstraint(AbstractConstraint):
  264. """Value must satisfy the type and presence constraints"""
  265. def _testValue(self, value, idx):
  266. if self.__singleTypeConstraint:
  267. self.__singleTypeConstraint(value)
  268. elif self.__multipleTypeConstraint:
  269. if idx not in self.__multipleTypeConstraint:
  270. raise error.ValueConstraintError(value)
  271. constraint, status = self.__multipleTypeConstraint[idx]
  272. if status == 'ABSENT': # XXX presense is not checked!
  273. raise error.ValueConstraintError(value)
  274. constraint(value)
  275. def _setValues(self, values):
  276. self.__multipleTypeConstraint = {}
  277. self.__singleTypeConstraint = None
  278. for v in values:
  279. if isinstance(v, tuple):
  280. self.__multipleTypeConstraint[v[0]] = v[1], v[2]
  281. else:
  282. self.__singleTypeConstraint = v
  283. AbstractConstraint._setValues(self, values)
  284. # Logic operations on constraints
  285. class ConstraintsExclusion(AbstractConstraint):
  286. """Create a ConstraintsExclusion logic operator object.
  287. The ConstraintsExclusion logic operator succeeds when the
  288. value does *not* satisfy the operand constraint.
  289. The ConstraintsExclusion object can be applied to
  290. any constraint and logic operator object.
  291. Parameters
  292. ----------
  293. constraint:
  294. Constraint or logic operator object.
  295. Examples
  296. --------
  297. .. code-block:: python
  298. class Lipogramme(IA5STRING):
  299. '''
  300. ASN.1 specification:
  301. Lipogramme ::=
  302. IA5String (FROM (ALL EXCEPT ("e"|"E")))
  303. '''
  304. subtypeSpec = ConstraintsExclusion(
  305. PermittedAlphabetConstraint('e', 'E')
  306. )
  307. # this will succeed
  308. lipogramme = Lipogramme('A work of fiction?')
  309. # this will raise ValueConstraintError
  310. lipogramme = Lipogramme('Eel')
  311. Warning
  312. -------
  313. The above example involving PermittedAlphabetConstraint might
  314. not work due to the way how PermittedAlphabetConstraint works.
  315. The other constraints might work with ConstraintsExclusion
  316. though.
  317. """
  318. def _testValue(self, value, idx):
  319. try:
  320. self._values[0](value, idx)
  321. except error.ValueConstraintError:
  322. return
  323. else:
  324. raise error.ValueConstraintError(value)
  325. def _setValues(self, values):
  326. if len(values) != 1:
  327. raise error.PyAsn1Error('Single constraint expected')
  328. AbstractConstraint._setValues(self, values)
  329. class AbstractConstraintSet(AbstractConstraint):
  330. def __getitem__(self, idx):
  331. return self._values[idx]
  332. def __iter__(self):
  333. return iter(self._values)
  334. def __add__(self, value):
  335. return self.__class__(*(self._values + (value,)))
  336. def __radd__(self, value):
  337. return self.__class__(*((value,) + self._values))
  338. def __len__(self):
  339. return len(self._values)
  340. # Constraints inclusion in sets
  341. def _setValues(self, values):
  342. self._values = values
  343. for constraint in values:
  344. if constraint:
  345. self._valueMap.add(constraint)
  346. self._valueMap.update(constraint.getValueMap())
  347. class ConstraintsIntersection(AbstractConstraintSet):
  348. """Create a ConstraintsIntersection logic operator object.
  349. The ConstraintsIntersection logic operator only succeeds
  350. if *all* its operands succeed.
  351. The ConstraintsIntersection object can be applied to
  352. any constraint and logic operator objects.
  353. The ConstraintsIntersection object duck-types the immutable
  354. container object like Python :py:class:`tuple`.
  355. Parameters
  356. ----------
  357. \*constraints:
  358. Constraint or logic operator objects.
  359. Examples
  360. --------
  361. .. code-block:: python
  362. class CapitalAndSmall(IA5String):
  363. '''
  364. ASN.1 specification:
  365. CapitalAndSmall ::=
  366. IA5String (FROM ("A".."Z"|"a".."z"))
  367. '''
  368. subtypeSpec = ConstraintsIntersection(
  369. PermittedAlphabetConstraint('A', 'Z'),
  370. PermittedAlphabetConstraint('a', 'z')
  371. )
  372. # this will succeed
  373. capital_and_small = CapitalAndSmall('Hello')
  374. # this will raise ValueConstraintError
  375. capital_and_small = CapitalAndSmall('hello')
  376. """
  377. def _testValue(self, value, idx):
  378. for constraint in self._values:
  379. constraint(value, idx)
  380. class ConstraintsUnion(AbstractConstraintSet):
  381. """Create a ConstraintsUnion logic operator object.
  382. The ConstraintsUnion logic operator only succeeds if
  383. *at least a single* operand succeeds.
  384. The ConstraintsUnion object can be applied to
  385. any constraint and logic operator objects.
  386. The ConstraintsUnion object duck-types the immutable
  387. container object like Python :py:class:`tuple`.
  388. Parameters
  389. ----------
  390. \*constraints:
  391. Constraint or logic operator objects.
  392. Examples
  393. --------
  394. .. code-block:: python
  395. class CapitalOrSmall(IA5String):
  396. '''
  397. ASN.1 specification:
  398. CapitalOrSmall ::=
  399. IA5String (FROM ("A".."Z") | FROM ("a".."z"))
  400. '''
  401. subtypeSpec = ConstraintsIntersection(
  402. PermittedAlphabetConstraint('A', 'Z'),
  403. PermittedAlphabetConstraint('a', 'z')
  404. )
  405. # this will succeed
  406. capital_or_small = CapitalAndSmall('Hello')
  407. # this will raise ValueConstraintError
  408. capital_or_small = CapitalOrSmall('hello!')
  409. """
  410. def _testValue(self, value, idx):
  411. for constraint in self._values:
  412. try:
  413. constraint(value, idx)
  414. except error.ValueConstraintError:
  415. pass
  416. else:
  417. return
  418. raise error.ValueConstraintError(
  419. 'all of %s failed for "%s"' % (self._values, value)
  420. )
  421. # TODO:
  422. # refactor InnerTypeConstraint
  423. # add tests for type check
  424. # implement other constraint types
  425. # make constraint validation easy to skip