Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pyasn1/type/constraint.py: 54%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

175 statements  

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 

10 

11from pyasn1.type import error 

12 

13__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint', 

14 'ValueRangeConstraint', 'ValueSizeConstraint', 

15 'PermittedAlphabetConstraint', 'InnerTypeConstraint', 

16 'ConstraintsExclusion', 'ConstraintsIntersection', 

17 'ConstraintsUnion'] 

18 

19 

20class AbstractConstraint(object): 

21 

22 def __init__(self, *values): 

23 self._valueMap = set() 

24 self._setValues(values) 

25 self.__hash = hash((self.__class__.__name__, self._values)) 

26 

27 def __call__(self, value, idx=None): 

28 if not self._values: 

29 return 

30 

31 try: 

32 self._testValue(value, idx) 

33 

34 except error.ValueConstraintError as exc: 

35 raise error.ValueConstraintError( 

36 '%s failed at: %r' % (self, exc) 

37 ) 

38 

39 def __repr__(self): 

40 representation = '%s object' % (self.__class__.__name__) 

41 

42 if self._values: 

43 representation += ', consts %s' % ', '.join( 

44 [repr(x) for x in self._values]) 

45 

46 return '<%s>' % representation 

47 

48 def __eq__(self, other): 

49 if self is other: 

50 return True 

51 return self._values == other 

52 

53 def __ne__(self, other): 

54 return self._values != other 

55 

56 def __lt__(self, other): 

57 return self._values < other 

58 

59 def __le__(self, other): 

60 return self._values <= other 

61 

62 def __gt__(self, other): 

63 return self._values > other 

64 

65 def __ge__(self, other): 

66 return self._values >= other 

67 

68 def __bool__(self): 

69 return bool(self._values) 

70 

71 def __hash__(self): 

72 return self.__hash 

73 

74 def _setValues(self, values): 

75 self._values = values 

76 

77 def _testValue(self, value, idx): 

78 raise error.ValueConstraintError(value) 

79 

80 # Constraints derivation logic 

81 def getValueMap(self): 

82 return self._valueMap 

83 

84 def isSuperTypeOf(self, otherConstraint): 

85 # TODO: fix possible comparison of set vs scalars here 

86 return (otherConstraint is self or 

87 not self._values or 

88 otherConstraint == self or 

89 self in otherConstraint.getValueMap()) 

90 

91 def isSubTypeOf(self, otherConstraint): 

92 return (otherConstraint is self or 

93 not self or 

94 otherConstraint == self or 

95 otherConstraint in self._valueMap) 

96 

97 

98class SingleValueConstraint(AbstractConstraint): 

99 """Create a SingleValueConstraint object. 

100 

101 The SingleValueConstraint satisfies any value that 

102 is present in the set of permitted values. 

103 

104 Objects of this type are iterable (emitting constraint values) and 

105 can act as operands for some arithmetic operations e.g. addition 

106 and subtraction. The latter can be used for combining multiple 

107 SingleValueConstraint objects into one. 

108 

109 The SingleValueConstraint object can be applied to 

110 any ASN.1 type. 

111 

112 Parameters 

113 ---------- 

114 *values: :class:`int` 

115 Full set of values permitted by this constraint object. 

116 

117 Examples 

118 -------- 

119 .. code-block:: python 

120 

121 class DivisorOfSix(Integer): 

122 ''' 

123 ASN.1 specification: 

124 

125 Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6) 

126 ''' 

127 subtypeSpec = SingleValueConstraint(1, 2, 3, 6) 

128 

129 # this will succeed 

130 divisor_of_six = DivisorOfSix(1) 

131 

132 # this will raise ValueConstraintError 

133 divisor_of_six = DivisorOfSix(7) 

134 """ 

135 def _setValues(self, values): 

136 self._values = values 

137 self._set = set(values) 

138 

139 def _testValue(self, value, idx): 

140 if value not in self._set: 

141 raise error.ValueConstraintError(value) 

142 

143 # Constrains can be merged or reduced 

144 

145 def __contains__(self, item): 

146 return item in self._set 

147 

148 def __iter__(self): 

149 return iter(self._set) 

150 

151 def __add__(self, constraint): 

152 return self.__class__(*(self._set.union(constraint))) 

153 

154 def __sub__(self, constraint): 

155 return self.__class__(*(self._set.difference(constraint))) 

156 

157 

158class ContainedSubtypeConstraint(AbstractConstraint): 

159 """Create a ContainedSubtypeConstraint object. 

160 

161 The ContainedSubtypeConstraint satisfies any value that 

162 is present in the set of permitted values and also 

163 satisfies included constraints. 

164 

165 The ContainedSubtypeConstraint object can be applied to 

166 any ASN.1 type. 

167 

168 Parameters 

169 ---------- 

170 *values: 

171 Full set of values and constraint objects permitted 

172 by this constraint object. 

173 

174 Examples 

175 -------- 

176 .. code-block:: python 

177 

178 class DivisorOfEighteen(Integer): 

179 ''' 

180 ASN.1 specification: 

181 

182 Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18) 

183 ''' 

184 subtypeSpec = ContainedSubtypeConstraint( 

185 SingleValueConstraint(1, 2, 3, 6), 9, 18 

186 ) 

187 

188 # this will succeed 

189 divisor_of_eighteen = DivisorOfEighteen(9) 

190 

191 # this will raise ValueConstraintError 

192 divisor_of_eighteen = DivisorOfEighteen(10) 

193 """ 

194 def _testValue(self, value, idx): 

195 for constraint in self._values: 

196 if isinstance(constraint, AbstractConstraint): 

197 constraint(value, idx) 

198 elif value not in self._set: 

199 raise error.ValueConstraintError(value) 

200 

201 

202class ValueRangeConstraint(AbstractConstraint): 

203 """Create a ValueRangeConstraint object. 

204 

205 The ValueRangeConstraint satisfies any value that 

206 falls in the range of permitted values. 

207 

208 The ValueRangeConstraint object can only be applied 

209 to :class:`~pyasn1.type.univ.Integer` and 

210 :class:`~pyasn1.type.univ.Real` types. 

211 

212 Parameters 

213 ---------- 

214 start: :class:`int` 

215 Minimum permitted value in the range (inclusive) 

216 

217 end: :class:`int` 

218 Maximum permitted value in the range (inclusive) 

219 

220 Examples 

221 -------- 

222 .. code-block:: python 

223 

224 class TeenAgeYears(Integer): 

225 ''' 

226 ASN.1 specification: 

227 

228 TeenAgeYears ::= INTEGER (13 .. 19) 

229 ''' 

230 subtypeSpec = ValueRangeConstraint(13, 19) 

231 

232 # this will succeed 

233 teen_year = TeenAgeYears(18) 

234 

235 # this will raise ValueConstraintError 

236 teen_year = TeenAgeYears(20) 

237 """ 

238 def _testValue(self, value, idx): 

239 if value < self.start or value > self.stop: 

240 raise error.ValueConstraintError(value) 

241 

242 def _setValues(self, values): 

243 if len(values) != 2: 

244 raise error.PyAsn1Error( 

245 '%s: bad constraint values' % (self.__class__.__name__,) 

246 ) 

247 self.start, self.stop = values 

248 if self.start > self.stop: 

249 raise error.PyAsn1Error( 

250 '%s: screwed constraint values (start > stop): %s > %s' % ( 

251 self.__class__.__name__, 

252 self.start, self.stop 

253 ) 

254 ) 

255 AbstractConstraint._setValues(self, values) 

256 

257 

258class ValueSizeConstraint(ValueRangeConstraint): 

259 """Create a ValueSizeConstraint object. 

260 

261 The ValueSizeConstraint satisfies any value for 

262 as long as its size falls within the range of 

263 permitted sizes. 

264 

265 The ValueSizeConstraint object can be applied 

266 to :class:`~pyasn1.type.univ.BitString`, 

267 :class:`~pyasn1.type.univ.OctetString` (including 

268 all :ref:`character ASN.1 types <type.char>`), 

269 :class:`~pyasn1.type.univ.SequenceOf` 

270 and :class:`~pyasn1.type.univ.SetOf` types. 

271 

272 Parameters 

273 ---------- 

274 minimum: :class:`int` 

275 Minimum permitted size of the value (inclusive) 

276 

277 maximum: :class:`int` 

278 Maximum permitted size of the value (inclusive) 

279 

280 Examples 

281 -------- 

282 .. code-block:: python 

283 

284 class BaseballTeamRoster(SetOf): 

285 ''' 

286 ASN.1 specification: 

287 

288 BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames 

289 ''' 

290 componentType = PlayerNames() 

291 subtypeSpec = ValueSizeConstraint(1, 25) 

292 

293 # this will succeed 

294 team = BaseballTeamRoster() 

295 team.extend(['Jan', 'Matej']) 

296 encode(team) 

297 

298 # this will raise ValueConstraintError 

299 team = BaseballTeamRoster() 

300 team.extend(['Jan'] * 26) 

301 encode(team) 

302 

303 Note 

304 ---- 

305 Whenever ValueSizeConstraint is applied to mutable types 

306 (e.g. :class:`~pyasn1.type.univ.SequenceOf`, 

307 :class:`~pyasn1.type.univ.SetOf`), constraint 

308 validation only happens at the serialisation phase rather 

309 than schema instantiation phase (as it is with immutable 

310 types). 

311 """ 

312 def _testValue(self, value, idx): 

313 valueSize = len(value) 

314 if valueSize < self.start or valueSize > self.stop: 

315 raise error.ValueConstraintError(value) 

316 

317 

318class PermittedAlphabetConstraint(SingleValueConstraint): 

319 """Create a PermittedAlphabetConstraint object. 

320 

321 The PermittedAlphabetConstraint satisfies any character 

322 string for as long as all its characters are present in 

323 the set of permitted characters. 

324 

325 Objects of this type are iterable (emitting constraint values) and 

326 can act as operands for some arithmetic operations e.g. addition 

327 and subtraction. 

328 

329 The PermittedAlphabetConstraint object can only be applied 

330 to the :ref:`character ASN.1 types <type.char>` such as 

331 :class:`~pyasn1.type.char.IA5String`. 

332 

333 Parameters 

334 ---------- 

335 *alphabet: :class:`str` 

336 Full set of characters permitted by this constraint object. 

337 

338 Example 

339 ------- 

340 .. code-block:: python 

341 

342 class BooleanValue(IA5String): 

343 ''' 

344 ASN.1 specification: 

345 

346 BooleanValue ::= IA5String (FROM ('T' | 'F')) 

347 ''' 

348 subtypeSpec = PermittedAlphabetConstraint('T', 'F') 

349 

350 # this will succeed 

351 truth = BooleanValue('T') 

352 truth = BooleanValue('TF') 

353 

354 # this will raise ValueConstraintError 

355 garbage = BooleanValue('TAF') 

356 

357 ASN.1 `FROM ... EXCEPT ...` clause can be modelled by combining multiple 

358 PermittedAlphabetConstraint objects into one: 

359 

360 Example 

361 ------- 

362 .. code-block:: python 

363 

364 class Lipogramme(IA5String): 

365 ''' 

366 ASN.1 specification: 

367 

368 Lipogramme ::= 

369 IA5String (FROM (ALL EXCEPT ("e"|"E"))) 

370 ''' 

371 subtypeSpec = ( 

372 PermittedAlphabetConstraint(*string.printable) - 

373 PermittedAlphabetConstraint('e', 'E') 

374 ) 

375 

376 # this will succeed 

377 lipogramme = Lipogramme('A work of fiction?') 

378 

379 # this will raise ValueConstraintError 

380 lipogramme = Lipogramme('Eel') 

381 

382 Note 

383 ---- 

384 Although `ConstraintsExclusion` object could seemingly be used for this 

385 purpose, practically, for it to work, it needs to represent its operand 

386 constraints as sets and intersect one with the other. That would require 

387 the insight into the constraint values (and their types) that are otherwise 

388 hidden inside the constraint object. 

389 

390 Therefore it's more practical to model `EXCEPT` clause at 

391 `PermittedAlphabetConstraint` level instead. 

392 """ 

393 def _setValues(self, values): 

394 self._values = values 

395 self._set = set(values) 

396 

397 def _testValue(self, value, idx): 

398 if not self._set.issuperset(value): 

399 raise error.ValueConstraintError(value) 

400 

401 

402class ComponentPresentConstraint(AbstractConstraint): 

403 """Create a ComponentPresentConstraint object. 

404 

405 The ComponentPresentConstraint is only satisfied when the value 

406 is not `None`. 

407 

408 The ComponentPresentConstraint object is typically used with 

409 `WithComponentsConstraint`. 

410 

411 Examples 

412 -------- 

413 .. code-block:: python 

414 

415 present = ComponentPresentConstraint() 

416 

417 # this will succeed 

418 present('whatever') 

419 

420 # this will raise ValueConstraintError 

421 present(None) 

422 """ 

423 def _setValues(self, values): 

424 self._values = ('<must be present>',) 

425 

426 if values: 

427 raise error.PyAsn1Error('No arguments expected') 

428 

429 def _testValue(self, value, idx): 

430 if value is None: 

431 raise error.ValueConstraintError( 

432 'Component is not present:') 

433 

434 

435class ComponentAbsentConstraint(AbstractConstraint): 

436 """Create a ComponentAbsentConstraint object. 

437 

438 The ComponentAbsentConstraint is only satisfied when the value 

439 is `None`. 

440 

441 The ComponentAbsentConstraint object is typically used with 

442 `WithComponentsConstraint`. 

443 

444 Examples 

445 -------- 

446 .. code-block:: python 

447 

448 absent = ComponentAbsentConstraint() 

449 

450 # this will succeed 

451 absent(None) 

452 

453 # this will raise ValueConstraintError 

454 absent('whatever') 

455 """ 

456 def _setValues(self, values): 

457 self._values = ('<must be absent>',) 

458 

459 if values: 

460 raise error.PyAsn1Error('No arguments expected') 

461 

462 def _testValue(self, value, idx): 

463 if value is not None: 

464 raise error.ValueConstraintError( 

465 'Component is not absent: %r' % value) 

466 

467 

468class WithComponentsConstraint(AbstractConstraint): 

469 """Create a WithComponentsConstraint object. 

470 

471 The `WithComponentsConstraint` satisfies any mapping object that has 

472 constrained fields present or absent, what is indicated by 

473 `ComponentPresentConstraint` and `ComponentAbsentConstraint` 

474 objects respectively. 

475 

476 The `WithComponentsConstraint` object is typically applied 

477 to :class:`~pyasn1.type.univ.Set` or 

478 :class:`~pyasn1.type.univ.Sequence` types. 

479 

480 Parameters 

481 ---------- 

482 *fields: :class:`tuple` 

483 Zero or more tuples of (`field`, `constraint`) indicating constrained 

484 fields. 

485 

486 Notes 

487 ----- 

488 On top of the primary use of `WithComponentsConstraint` (ensuring presence 

489 or absence of particular components of a :class:`~pyasn1.type.univ.Set` or 

490 :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other 

491 constraint objects or their combinations. In case of scalar fields, these 

492 constraints will be verified in addition to the constraints belonging to 

493 scalar components themselves. However, formally, these additional 

494 constraints do not change the type of these ASN.1 objects. 

495 

496 Examples 

497 -------- 

498 

499 .. code-block:: python 

500 

501 class Item(Sequence): # Set is similar 

502 ''' 

503 ASN.1 specification: 

504 

505 Item ::= SEQUENCE { 

506 id INTEGER OPTIONAL, 

507 name OCTET STRING OPTIONAL 

508 } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT 

509 ''' 

510 componentType = NamedTypes( 

511 OptionalNamedType('id', Integer()), 

512 OptionalNamedType('name', OctetString()) 

513 ) 

514 withComponents = ConstraintsUnion( 

515 WithComponentsConstraint( 

516 ('id', ComponentPresentConstraint()), 

517 ('name', ComponentAbsentConstraint()) 

518 ), 

519 WithComponentsConstraint( 

520 ('id', ComponentAbsentConstraint()), 

521 ('name', ComponentPresentConstraint()) 

522 ) 

523 ) 

524 

525 item = Item() 

526 

527 # This will succeed 

528 item['id'] = 1 

529 

530 # This will succeed 

531 item.reset() 

532 item['name'] = 'John' 

533 

534 # This will fail (on encoding) 

535 item.reset() 

536 descr['id'] = 1 

537 descr['name'] = 'John' 

538 """ 

539 def _testValue(self, value, idx): 

540 for field, constraint in self._values: 

541 constraint(value.get(field)) 

542 

543 def _setValues(self, values): 

544 AbstractConstraint._setValues(self, values) 

545 

546 

547# This is a bit kludgy, meaning two op modes within a single constraint 

548class InnerTypeConstraint(AbstractConstraint): 

549 """Value must satisfy the type and presence constraints""" 

550 

551 def _testValue(self, value, idx): 

552 if self.__singleTypeConstraint: 

553 self.__singleTypeConstraint(value) 

554 elif self.__multipleTypeConstraint: 

555 if idx not in self.__multipleTypeConstraint: 

556 raise error.ValueConstraintError(value) 

557 constraint, status = self.__multipleTypeConstraint[idx] 

558 if status == 'ABSENT': # XXX presence is not checked! 

559 raise error.ValueConstraintError(value) 

560 constraint(value) 

561 

562 def _setValues(self, values): 

563 self.__multipleTypeConstraint = {} 

564 self.__singleTypeConstraint = None 

565 for v in values: 

566 if isinstance(v, tuple): 

567 self.__multipleTypeConstraint[v[0]] = v[1], v[2] 

568 else: 

569 self.__singleTypeConstraint = v 

570 AbstractConstraint._setValues(self, values) 

571 

572 

573# Logic operations on constraints 

574 

575class ConstraintsExclusion(AbstractConstraint): 

576 """Create a ConstraintsExclusion logic operator object. 

577 

578 The ConstraintsExclusion logic operator succeeds when the 

579 value does *not* satisfy the operand constraint. 

580 

581 The ConstraintsExclusion object can be applied to 

582 any constraint and logic operator object. 

583 

584 Parameters 

585 ---------- 

586 *constraints: 

587 Constraint or logic operator objects. 

588 

589 Examples 

590 -------- 

591 .. code-block:: python 

592 

593 class LuckyNumber(Integer): 

594 subtypeSpec = ConstraintsExclusion( 

595 SingleValueConstraint(13) 

596 ) 

597 

598 # this will succeed 

599 luckyNumber = LuckyNumber(12) 

600 

601 # this will raise ValueConstraintError 

602 luckyNumber = LuckyNumber(13) 

603 

604 Note 

605 ---- 

606 The `FROM ... EXCEPT ...` ASN.1 clause should be modeled by combining 

607 constraint objects into one. See `PermittedAlphabetConstraint` for more 

608 information. 

609 """ 

610 def _testValue(self, value, idx): 

611 for constraint in self._values: 

612 try: 

613 constraint(value, idx) 

614 

615 except error.ValueConstraintError: 

616 continue 

617 

618 raise error.ValueConstraintError(value) 

619 

620 def _setValues(self, values): 

621 AbstractConstraint._setValues(self, values) 

622 

623 

624class AbstractConstraintSet(AbstractConstraint): 

625 

626 def __getitem__(self, idx): 

627 return self._values[idx] 

628 

629 def __iter__(self): 

630 return iter(self._values) 

631 

632 def __add__(self, value): 

633 return self.__class__(*(self._values + (value,))) 

634 

635 def __radd__(self, value): 

636 return self.__class__(*((value,) + self._values)) 

637 

638 def __len__(self): 

639 return len(self._values) 

640 

641 # Constraints inclusion in sets 

642 

643 def _setValues(self, values): 

644 self._values = values 

645 for constraint in values: 

646 if constraint: 

647 self._valueMap.add(constraint) 

648 self._valueMap.update(constraint.getValueMap()) 

649 

650 

651class ConstraintsIntersection(AbstractConstraintSet): 

652 """Create a ConstraintsIntersection logic operator object. 

653 

654 The ConstraintsIntersection logic operator only succeeds 

655 if *all* its operands succeed. 

656 

657 The ConstraintsIntersection object can be applied to 

658 any constraint and logic operator objects. 

659 

660 The ConstraintsIntersection object duck-types the immutable 

661 container object like Python :py:class:`tuple`. 

662 

663 Parameters 

664 ---------- 

665 *constraints: 

666 Constraint or logic operator objects. 

667 

668 Examples 

669 -------- 

670 .. code-block:: python 

671 

672 class CapitalAndSmall(IA5String): 

673 ''' 

674 ASN.1 specification: 

675 

676 CapitalAndSmall ::= 

677 IA5String (FROM ("A".."Z"|"a".."z")) 

678 ''' 

679 subtypeSpec = ConstraintsIntersection( 

680 PermittedAlphabetConstraint('A', 'Z'), 

681 PermittedAlphabetConstraint('a', 'z') 

682 ) 

683 

684 # this will succeed 

685 capital_and_small = CapitalAndSmall('Hello') 

686 

687 # this will raise ValueConstraintError 

688 capital_and_small = CapitalAndSmall('hello') 

689 """ 

690 def _testValue(self, value, idx): 

691 for constraint in self._values: 

692 constraint(value, idx) 

693 

694 

695class ConstraintsUnion(AbstractConstraintSet): 

696 """Create a ConstraintsUnion logic operator object. 

697 

698 The ConstraintsUnion logic operator succeeds if 

699 *at least* a single operand succeeds. 

700 

701 The ConstraintsUnion object can be applied to 

702 any constraint and logic operator objects. 

703 

704 The ConstraintsUnion object duck-types the immutable 

705 container object like Python :py:class:`tuple`. 

706 

707 Parameters 

708 ---------- 

709 *constraints: 

710 Constraint or logic operator objects. 

711 

712 Examples 

713 -------- 

714 .. code-block:: python 

715 

716 class CapitalOrSmall(IA5String): 

717 ''' 

718 ASN.1 specification: 

719 

720 CapitalOrSmall ::= 

721 IA5String (FROM ("A".."Z") | FROM ("a".."z")) 

722 ''' 

723 subtypeSpec = ConstraintsUnion( 

724 PermittedAlphabetConstraint('A', 'Z'), 

725 PermittedAlphabetConstraint('a', 'z') 

726 ) 

727 

728 # this will succeed 

729 capital_or_small = CapitalAndSmall('Hello') 

730 

731 # this will raise ValueConstraintError 

732 capital_or_small = CapitalOrSmall('hello!') 

733 """ 

734 def _testValue(self, value, idx): 

735 for constraint in self._values: 

736 try: 

737 constraint(value, idx) 

738 except error.ValueConstraintError: 

739 pass 

740 else: 

741 return 

742 

743 raise error.ValueConstraintError( 

744 'all of %s failed for "%s"' % (self._values, value) 

745 ) 

746 

747# TODO: 

748# refactor InnerTypeConstraint 

749# add tests for type check 

750# implement other constraint types 

751# make constraint validation easy to skip