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:51 +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 

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: 

35 raise error.ValueConstraintError( 

36 '%s failed at: %r' % (self, sys.exc_info()[1]) 

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 return self is other and True or self._values == other 

50 

51 def __ne__(self, other): 

52 return self._values != other 

53 

54 def __lt__(self, other): 

55 return self._values < other 

56 

57 def __le__(self, other): 

58 return self._values <= other 

59 

60 def __gt__(self, other): 

61 return self._values > other 

62 

63 def __ge__(self, other): 

64 return self._values >= other 

65 

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 

72 

73 def __hash__(self): 

74 return self.__hash 

75 

76 def _setValues(self, values): 

77 self._values = values 

78 

79 def _testValue(self, value, idx): 

80 raise error.ValueConstraintError(value) 

81 

82 # Constraints derivation logic 

83 def getValueMap(self): 

84 return self._valueMap 

85 

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()) 

92 

93 def isSubTypeOf(self, otherConstraint): 

94 return (otherConstraint is self or 

95 not self or 

96 otherConstraint == self or 

97 otherConstraint in self._valueMap) 

98 

99 

100class SingleValueConstraint(AbstractConstraint): 

101 """Create a SingleValueConstraint object. 

102 

103 The SingleValueConstraint satisfies any value that 

104 is present in the set of permitted values. 

105 

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. 

110 

111 The SingleValueConstraint object can be applied to 

112 any ASN.1 type. 

113 

114 Parameters 

115 ---------- 

116 *values: :class:`int` 

117 Full set of values permitted by this constraint object. 

118 

119 Examples 

120 -------- 

121 .. code-block:: python 

122 

123 class DivisorOfSix(Integer): 

124 ''' 

125 ASN.1 specification: 

126 

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

128 ''' 

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

130 

131 # this will succeed 

132 divisor_of_six = DivisorOfSix(1) 

133 

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) 

140 

141 def _testValue(self, value, idx): 

142 if value not in self._set: 

143 raise error.ValueConstraintError(value) 

144 

145 # Constrains can be merged or reduced 

146 

147 def __contains__(self, item): 

148 return item in self._set 

149 

150 def __iter__(self): 

151 return iter(self._set) 

152 

153 def __sub__(self, constraint): 

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

155 

156 def __add__(self, constraint): 

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

158 

159 def __sub__(self, constraint): 

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

161 

162 

163class ContainedSubtypeConstraint(AbstractConstraint): 

164 """Create a ContainedSubtypeConstraint object. 

165 

166 The ContainedSubtypeConstraint satisfies any value that 

167 is present in the set of permitted values and also 

168 satisfies included constraints. 

169 

170 The ContainedSubtypeConstraint object can be applied to 

171 any ASN.1 type. 

172 

173 Parameters 

174 ---------- 

175 *values: 

176 Full set of values and constraint objects permitted 

177 by this constraint object. 

178 

179 Examples 

180 -------- 

181 .. code-block:: python 

182 

183 class DivisorOfEighteen(Integer): 

184 ''' 

185 ASN.1 specification: 

186 

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

188 ''' 

189 subtypeSpec = ContainedSubtypeConstraint( 

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

191 ) 

192 

193 # this will succeed 

194 divisor_of_eighteen = DivisorOfEighteen(9) 

195 

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) 

205 

206 

207class ValueRangeConstraint(AbstractConstraint): 

208 """Create a ValueRangeConstraint object. 

209 

210 The ValueRangeConstraint satisfies any value that 

211 falls in the range of permitted values. 

212 

213 The ValueRangeConstraint object can only be applied 

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

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

216 

217 Parameters 

218 ---------- 

219 start: :class:`int` 

220 Minimum permitted value in the range (inclusive) 

221 

222 end: :class:`int` 

223 Maximum permitted value in the range (inclusive) 

224 

225 Examples 

226 -------- 

227 .. code-block:: python 

228 

229 class TeenAgeYears(Integer): 

230 ''' 

231 ASN.1 specification: 

232 

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

234 ''' 

235 subtypeSpec = ValueRangeConstraint(13, 19) 

236 

237 # this will succeed 

238 teen_year = TeenAgeYears(18) 

239 

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) 

246 

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) 

261 

262 

263class ValueSizeConstraint(ValueRangeConstraint): 

264 """Create a ValueSizeConstraint object. 

265 

266 The ValueSizeConstraint satisfies any value for 

267 as long as its size falls within the range of 

268 permitted sizes. 

269 

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. 

276 

277 Parameters 

278 ---------- 

279 minimum: :class:`int` 

280 Minimum permitted size of the value (inclusive) 

281 

282 maximum: :class:`int` 

283 Maximum permitted size of the value (inclusive) 

284 

285 Examples 

286 -------- 

287 .. code-block:: python 

288 

289 class BaseballTeamRoster(SetOf): 

290 ''' 

291 ASN.1 specification: 

292 

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

294 ''' 

295 componentType = PlayerNames() 

296 subtypeSpec = ValueSizeConstraint(1, 25) 

297 

298 # this will succeed 

299 team = BaseballTeamRoster() 

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

301 encode(team) 

302 

303 # this will raise ValueConstraintError 

304 team = BaseballTeamRoster() 

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

306 encode(team) 

307 

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) 

321 

322 

323class PermittedAlphabetConstraint(SingleValueConstraint): 

324 """Create a PermittedAlphabetConstraint object. 

325 

326 The PermittedAlphabetConstraint satisfies any character 

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

328 the set of permitted characters. 

329 

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. 

333 

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`. 

337 

338 Parameters 

339 ---------- 

340 *alphabet: :class:`str` 

341 Full set of characters permitted by this constraint object. 

342 

343 Example 

344 ------- 

345 .. code-block:: python 

346 

347 class BooleanValue(IA5String): 

348 ''' 

349 ASN.1 specification: 

350 

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

352 ''' 

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

354 

355 # this will succeed 

356 truth = BooleanValue('T') 

357 truth = BooleanValue('TF') 

358 

359 # this will raise ValueConstraintError 

360 garbage = BooleanValue('TAF') 

361 

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

363 PermittedAlphabetConstraint objects into one: 

364 

365 Example 

366 ------- 

367 .. code-block:: python 

368 

369 class Lipogramme(IA5String): 

370 ''' 

371 ASN.1 specification: 

372 

373 Lipogramme ::= 

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

375 ''' 

376 subtypeSpec = ( 

377 PermittedAlphabetConstraint(*string.printable) - 

378 PermittedAlphabetConstraint('e', 'E') 

379 ) 

380 

381 # this will succeed 

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

383 

384 # this will raise ValueConstraintError 

385 lipogramme = Lipogramme('Eel') 

386 

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. 

394 

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) 

401 

402 def _testValue(self, value, idx): 

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

404 raise error.ValueConstraintError(value) 

405 

406 

407class ComponentPresentConstraint(AbstractConstraint): 

408 """Create a ComponentPresentConstraint object. 

409 

410 The ComponentPresentConstraint is only satisfied when the value 

411 is not `None`. 

412 

413 The ComponentPresentConstraint object is typically used with 

414 `WithComponentsConstraint`. 

415 

416 Examples 

417 -------- 

418 .. code-block:: python 

419 

420 present = ComponentPresentConstraint() 

421 

422 # this will succeed 

423 present('whatever') 

424 

425 # this will raise ValueConstraintError 

426 present(None) 

427 """ 

428 def _setValues(self, values): 

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

430 

431 if values: 

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

433 

434 def _testValue(self, value, idx): 

435 if value is None: 

436 raise error.ValueConstraintError( 

437 'Component is not present:') 

438 

439 

440class ComponentAbsentConstraint(AbstractConstraint): 

441 """Create a ComponentAbsentConstraint object. 

442 

443 The ComponentAbsentConstraint is only satisfied when the value 

444 is `None`. 

445 

446 The ComponentAbsentConstraint object is typically used with 

447 `WithComponentsConstraint`. 

448 

449 Examples 

450 -------- 

451 .. code-block:: python 

452 

453 absent = ComponentAbsentConstraint() 

454 

455 # this will succeed 

456 absent(None) 

457 

458 # this will raise ValueConstraintError 

459 absent('whatever') 

460 """ 

461 def _setValues(self, values): 

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

463 

464 if values: 

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

466 

467 def _testValue(self, value, idx): 

468 if value is not None: 

469 raise error.ValueConstraintError( 

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

471 

472 

473class WithComponentsConstraint(AbstractConstraint): 

474 """Create a WithComponentsConstraint object. 

475 

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. 

480 

481 The `WithComponentsConstraint` object is typically applied 

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

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

484 

485 Parameters 

486 ---------- 

487 *fields: :class:`tuple` 

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

489 fields. 

490 

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. 

500 

501 Examples 

502 -------- 

503 

504 .. code-block:: python 

505 

506 class Item(Sequence): # Set is similar 

507 ''' 

508 ASN.1 specification: 

509 

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 ) 

529 

530 item = Item() 

531 

532 # This will succeed 

533 item['id'] = 1 

534 

535 # This will succeed 

536 item.reset() 

537 item['name'] = 'John' 

538 

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)) 

547 

548 def _setValues(self, values): 

549 AbstractConstraint._setValues(self, values) 

550 

551 

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""" 

555 

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) 

566 

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) 

576 

577 

578# Logic operations on constraints 

579 

580class ConstraintsExclusion(AbstractConstraint): 

581 """Create a ConstraintsExclusion logic operator object. 

582 

583 The ConstraintsExclusion logic operator succeeds when the 

584 value does *not* satisfy the operand constraint. 

585 

586 The ConstraintsExclusion object can be applied to 

587 any constraint and logic operator object. 

588 

589 Parameters 

590 ---------- 

591 *constraints: 

592 Constraint or logic operator objects. 

593 

594 Examples 

595 -------- 

596 .. code-block:: python 

597 

598 class LuckyNumber(Integer): 

599 subtypeSpec = ConstraintsExclusion( 

600 SingleValueConstraint(13) 

601 ) 

602 

603 # this will succeed 

604 luckyNumber = LuckyNumber(12) 

605 

606 # this will raise ValueConstraintError 

607 luckyNumber = LuckyNumber(13) 

608 

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) 

619 

620 except error.ValueConstraintError: 

621 continue 

622 

623 raise error.ValueConstraintError(value) 

624 

625 def _setValues(self, values): 

626 AbstractConstraint._setValues(self, values) 

627 

628 

629class AbstractConstraintSet(AbstractConstraint): 

630 

631 def __getitem__(self, idx): 

632 return self._values[idx] 

633 

634 def __iter__(self): 

635 return iter(self._values) 

636 

637 def __add__(self, value): 

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

639 

640 def __radd__(self, value): 

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

642 

643 def __len__(self): 

644 return len(self._values) 

645 

646 # Constraints inclusion in sets 

647 

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()) 

654 

655 

656class ConstraintsIntersection(AbstractConstraintSet): 

657 """Create a ConstraintsIntersection logic operator object. 

658 

659 The ConstraintsIntersection logic operator only succeeds 

660 if *all* its operands succeed. 

661 

662 The ConstraintsIntersection object can be applied to 

663 any constraint and logic operator objects. 

664 

665 The ConstraintsIntersection object duck-types the immutable 

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

667 

668 Parameters 

669 ---------- 

670 *constraints: 

671 Constraint or logic operator objects. 

672 

673 Examples 

674 -------- 

675 .. code-block:: python 

676 

677 class CapitalAndSmall(IA5String): 

678 ''' 

679 ASN.1 specification: 

680 

681 CapitalAndSmall ::= 

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

683 ''' 

684 subtypeSpec = ConstraintsIntersection( 

685 PermittedAlphabetConstraint('A', 'Z'), 

686 PermittedAlphabetConstraint('a', 'z') 

687 ) 

688 

689 # this will succeed 

690 capital_and_small = CapitalAndSmall('Hello') 

691 

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) 

698 

699 

700class ConstraintsUnion(AbstractConstraintSet): 

701 """Create a ConstraintsUnion logic operator object. 

702 

703 The ConstraintsUnion logic operator succeeds if 

704 *at least* a single operand succeeds. 

705 

706 The ConstraintsUnion object can be applied to 

707 any constraint and logic operator objects. 

708 

709 The ConstraintsUnion object duck-types the immutable 

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

711 

712 Parameters 

713 ---------- 

714 *constraints: 

715 Constraint or logic operator objects. 

716 

717 Examples 

718 -------- 

719 .. code-block:: python 

720 

721 class CapitalOrSmall(IA5String): 

722 ''' 

723 ASN.1 specification: 

724 

725 CapitalOrSmall ::= 

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

727 ''' 

728 subtypeSpec = ConstraintsUnion( 

729 PermittedAlphabetConstraint('A', 'Z'), 

730 PermittedAlphabetConstraint('a', 'z') 

731 ) 

732 

733 # this will succeed 

734 capital_or_small = CapitalAndSmall('Hello') 

735 

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 

747 

748 raise error.ValueConstraintError( 

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

750 ) 

751 

752# TODO: 

753# refactor InnerTypeConstraint 

754# add tests for type check 

755# implement other constraint types 

756# make constraint validation easy to skip