Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# coding=utf-8 

2import copy 

3import datetime 

4import decimal 

5import functools 

6import itertools 

7import pprint 

8import re 

9import translationstring 

10import warnings 

11import types 

12 

13from iso8601 import iso8601 

14 

15from .compat import text_, text_type, string_types, xrange, is_nonstr_iter 

16 

17 

18_ = translationstring.TranslationStringFactory('colander') 

19 

20 

21class _required(object): 

22 """ Represents a required value in colander-related operations. """ 

23 

24 def __repr__(self): 

25 return '<colander.required>' 

26 

27 def __reduce__(self): 

28 # when unpickled, refers to "required" below (singleton) 

29 return 'required' 

30 

31 

32required = _required() 

33_marker = required # bw compat 

34 

35 

36class _null(object): 

37 """ Represents a null value in colander-related operations. """ 

38 

39 def __nonzero__(self): 

40 return False 

41 

42 # py3 compat 

43 __bool__ = __nonzero__ 

44 

45 def __repr__(self): 

46 return '<colander.null>' 

47 

48 def __reduce__(self): 

49 return 'null' # when unpickled, refers to "null" below (singleton) 

50 

51 

52null = _null() 

53 

54 

55class _drop(object): 

56 """ Represents a value that will be dropped from the schema if it 

57 is missing during *serialization* or *deserialization*. Passed as 

58 a value to the `missing` or `default` keyword argument 

59 of :class:`SchemaNode`. 

60 """ 

61 

62 def __repr__(self): 

63 return '<colander.drop>' 

64 

65 def __reduce__(self): 

66 return 'drop' # when unpickled, refers to "drop" below (singleton) 

67 

68 

69drop = _drop() 

70 

71 

72def interpolate(msgs): 

73 for s in msgs: 

74 if hasattr(s, 'interpolate'): 

75 yield s.interpolate() 

76 else: 

77 yield s 

78 

79 

80class UnboundDeferredError(Exception): 

81 """ 

82 An exception raised by :meth:`SchemaNode.deserialize` when an attempt 

83 is made to deserialize a node which has an unbound :class:`deferred` 

84 validator. 

85 """ 

86 

87 

88class Invalid(Exception): 

89 """ 

90 An exception raised by data types and validators indicating that 

91 the value for a particular node was not valid. 

92 

93 The constructor receives a mandatory ``node`` argument. This must 

94 be an instance of the :class:`colander.SchemaNode` class, or at 

95 least something with the same interface. 

96 

97 The constructor also receives an optional ``msg`` keyword 

98 argument, defaulting to ``None``. The ``msg`` argument is a 

99 freeform field indicating the error circumstance. 

100 

101 The constructor additionally may receive an optional ``value`` 

102 keyword, indicating the value related to the error. 

103 """ 

104 

105 pos = None 

106 positional = False 

107 

108 def __init__(self, node, msg=None, value=None): 

109 Exception.__init__(self, node, msg) 

110 self.node = node 

111 self.msg = msg 

112 self.value = value 

113 self.children = [] 

114 

115 def messages(self): 

116 """ Return an iterable of error messages for this exception using the 

117 ``msg`` attribute of this error node. If the ``msg`` attribute is 

118 iterable, it is returned. If it is not iterable, and is 

119 non-``None``, a single-element list containing the ``msg`` value is 

120 returned. If the value is ``None``, an empty list is returned.""" 

121 if is_nonstr_iter(self.msg): 

122 return self.msg 

123 if self.msg is None: 

124 return [] 

125 return [self.msg] 

126 

127 def add(self, exc, pos=None): 

128 """ Add a child exception; ``exc`` must be an instance of 

129 :class:`colander.Invalid` or a subclass. 

130 

131 ``pos`` is a value important for accurate error reporting. If 

132 it is provided, it must be an integer representing the 

133 position of ``exc`` relative to all other subexceptions of 

134 this exception node. For example, if the exception being 

135 added is about the third child of the exception which is 

136 ``self``, ``pos`` might be passed as ``3``. 

137 

138 If ``pos`` is provided, it will be assigned to the ``pos`` 

139 attribute of the provided ``exc`` object. 

140 """ 

141 if self.node and isinstance(self.node.typ, Positional): 

142 exc.positional = True 

143 if pos is not None: 

144 exc.pos = pos 

145 self.children.append(exc) 

146 

147 def __setitem__(self, name, msg): 

148 """ Add a subexception related to a child node with the 

149 message ``msg``. ``name`` must be present in the names of the 

150 set of child nodes of this exception's node; if this is not 

151 so, a :exc:`KeyError` is raised. 

152 

153 For example, if the exception upon which ``__setitem__`` is 

154 called has a node attribute, and that node attribute has 

155 children that have the names ``name`` and ``title``, you may 

156 successfully call ``__setitem__('name', 'Bad name')`` or 

157 ``__setitem__('title', 'Bad title')``. But calling 

158 ``__setitem__('wrong', 'whoops')`` will result in a 

159 :exc:`KeyError`. 

160 

161 This method is typically only useful if the ``node`` attribute 

162 of the exception upon which it is called is a schema node 

163 representing a mapping. 

164 """ 

165 for num, child in enumerate(self.node.children): 

166 if child.name == name: 

167 exc = Invalid(child, msg) 

168 self.add(exc, num) 

169 return 

170 raise KeyError(name) 

171 

172 def paths(self): 

173 """ A generator which returns each path through the exception 

174 graph. Each path is represented as a tuple of exception 

175 nodes. Within each tuple, the leftmost item will represent 

176 the root schema node, the rightmost item will represent the 

177 leaf schema node.""" 

178 

179 def traverse(node, stack): 

180 stack.append(node) 

181 

182 if not node.children: 

183 yield tuple(stack) 

184 

185 for child in node.children: 

186 for path in traverse(child, stack): 

187 yield path 

188 

189 stack.pop() 

190 

191 return traverse(self, []) 

192 

193 def _keyname(self): 

194 if self.positional: 

195 return str(self.pos) 

196 return str(self.node.name) 

197 

198 def asdict(self, translate=None, separator='; '): 

199 """ Return a dictionary containing a basic 

200 (non-language-translated) error report for this exception. 

201 

202 If ``translate`` is supplied, it must be a callable taking a 

203 translation string as its sole argument and returning a localized, 

204 interpolated string. 

205 

206 If ``separator`` is supplied, error messages are joined with that. 

207 """ 

208 paths = self.paths() 

209 errors = {} 

210 for path in paths: 

211 keyparts = [] 

212 msgs = [] 

213 for exc in path: 

214 exc.msg and msgs.extend(exc.messages()) 

215 keyname = exc._keyname() 

216 keyname and keyparts.append(keyname) 

217 if translate: 

218 msgs = [translate(msg) for msg in msgs] 

219 msgs = interpolate(msgs) 

220 if separator: 

221 msgs = separator.join(msgs) 

222 else: 

223 msgs = list(msgs) 

224 errors['.'.join(keyparts)] = msgs 

225 return errors 

226 

227 def __str__(self): 

228 """ Return a pretty-formatted string representation of the 

229 result of an execution of this exception's ``asdict`` method""" 

230 return pprint.pformat(self.asdict()) 

231 

232 

233class UnsupportedFields(Invalid): 

234 """ 

235 Exception used when schema object detect unknown fields in the 

236 cstruct during deserialize. 

237 """ 

238 

239 def __init__(self, node, fields, msg=None): 

240 super(UnsupportedFields, self).__init__(node, msg) 

241 self.fields = fields 

242 

243 

244class All(object): 

245 """ Composite validator which succeeds if none of its 

246 subvalidators raises an :class:`colander.Invalid` exception""" 

247 

248 def __init__(self, *validators): 

249 self.validators = validators 

250 

251 def __call__(self, node, value): 

252 excs = [] 

253 for validator in self.validators: 

254 try: 

255 validator(node, value) 

256 except Invalid as e: 

257 excs.append(e) 

258 

259 if excs: 

260 exc = Invalid(node, [exc.msg for exc in excs]) 

261 for e in excs: 

262 exc.children.extend(e.children) 

263 raise exc 

264 

265 

266class Any(All): 

267 """ Composite validator which succeeds if at least one of its 

268 subvalidators does not raise an :class:`colander.Invalid` exception.""" 

269 

270 def __call__(self, node, value): 

271 try: 

272 return super(Any, self).__call__(node, value) 

273 except Invalid as e: 

274 if len(e.msg) < len(self.validators): 

275 # At least one validator did not fail: 

276 return 

277 raise 

278 

279 

280class Function(object): 

281 """ Validator which accepts a function and an optional message; 

282 the function is called with the ``value`` during validation. 

283 

284 If the function returns anything falsy (``None``, ``False``, the 

285 empty string, ``0``, an object with a ``__nonzero__`` that returns 

286 ``False``, etc) when called during validation, an 

287 :exc:`colander.Invalid` exception is raised (validation fails); 

288 its msg will be the value of the ``msg`` argument passed to this 

289 class' constructor. 

290 

291 If the function returns a stringlike object (a ``str`` or 

292 ``unicode`` object) that is *not* the empty string , a 

293 :exc:`colander.Invalid` exception is raised using the stringlike 

294 value returned from the function as the exeption message 

295 (validation fails). 

296 

297 If the function returns anything *except* a stringlike object 

298 object which is truthy (e.g. ``True``, the integer ``1``, an 

299 object with a ``__nonzero__`` that returns ``True``, etc), an 

300 :exc:`colander.Invalid` exception is *not* raised (validation 

301 succeeds). 

302 

303 The default value for the ``msg`` when not provided via the 

304 constructor is ``Invalid value``. 

305 

306 The ``message`` parameter has been deprecated, use ``msg`` instead. 

307 """ 

308 

309 def __init__(self, function, msg=None, message=None): 

310 self.function = function 

311 if msg is not None and message is not None: 

312 raise ValueError('Only one of msg and message can be passed') 

313 # Handle bw compat 

314 if msg is None and message is None: 

315 msg = _('Invalid value') 

316 elif message is not None: 

317 warnings.warn( 

318 'The "message" argument has been deprecated, use "msg" ' 

319 'instead.', 

320 DeprecationWarning, 

321 ) 

322 msg = message 

323 self.msg = msg 

324 

325 def __call__(self, node, value): 

326 result = self.function(value) 

327 if not result: 

328 raise Invalid( 

329 node, 

330 translationstring.TranslationString( 

331 self.msg, mapping={'val': value} 

332 ), 

333 ) 

334 if isinstance(result, string_types): 

335 raise Invalid( 

336 node, 

337 translationstring.TranslationString( 

338 result, mapping={'val': value} 

339 ), 

340 ) 

341 

342 

343class Regex(object): 

344 """ Regular expression validator. 

345 

346 Initialize it with the string regular expression ``regex`` that will 

347 be compiled and matched against ``value`` when validator is called. It 

348 uses Python's :py:func:`re.match`, which only matches at the beginning 

349 of the string and not at the beginning of each line. To match the 

350 entire string, enclose the regular expression with ``^`` and ``$``. 

351 If ``msg`` is supplied, it will be the error message to be used; 

352 otherwise, defaults to 'String does not match expected pattern'. 

353 

354 The ``regex`` expression behaviour can be modified by specifying 

355 any ``flags`` value taken by ``re.compile``. 

356 

357 The ``regex`` argument may also be a pattern object (the 

358 result of ``re.compile``) instead of a string. 

359 

360 When calling, if ``value`` matches the regular expression, 

361 validation succeeds; otherwise, :exc:`colander.Invalid` is 

362 raised with the ``msg`` error message. 

363 """ 

364 

365 def __init__(self, regex, msg=None, flags=0): 

366 if isinstance(regex, string_types): 

367 self.match_object = re.compile(regex, flags) 

368 else: 

369 self.match_object = regex 

370 if msg is None: 

371 self.msg = _("String does not match expected pattern") 

372 else: 

373 self.msg = msg 

374 

375 def __call__(self, node, value): 

376 if self.match_object.match(value) is None: 

377 raise Invalid(node, self.msg) 

378 

379 

380# Regex for email addresses. 

381# 

382# Stolen from the WhatWG HTML spec: 

383# https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type=email) 

384# 

385# If it is good enough for browsers, it is good enough for us! 

386EMAIL_RE = ( 

387 r"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9]" 

388 r"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9]" 

389 r"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" 

390) 

391 

392 

393class Email(Regex): 

394 """ Email address validator. If ``msg`` is supplied, it will be 

395 the error message to be used when raising :exc:`colander.Invalid`; 

396 otherwise, defaults to 'Invalid email address'. 

397 """ 

398 

399 def __init__(self, msg=None): 

400 email_regex = text_(EMAIL_RE) 

401 if msg is None: 

402 msg = _("Invalid email address") 

403 super(Email, self).__init__(email_regex, msg=msg) 

404 

405 

406class Range(object): 

407 """ Validator which succeeds if the value it is passed is greater 

408 or equal to ``min`` and less than or equal to ``max``. If ``min`` 

409 is not specified, or is specified as ``None``, no lower bound 

410 exists. If ``max`` is not specified, or is specified as ``None``, 

411 no upper bound exists. 

412 

413 ``min_err`` is used to form the ``msg`` of the 

414 :exc:`colander.Invalid` error when reporting a validation failure 

415 caused by a value not meeting the minimum. If ``min_err`` is 

416 specified, it must be a string. The string may contain the 

417 replacement targets ``${min}`` and ``${val}``, representing the 

418 minimum value and the provided value respectively. If it is not 

419 provided, it defaults to ``'${val} is less than minimum value 

420 ${min}'``. 

421 

422 ``max_err`` is used to form the ``msg`` of the 

423 :exc:`colander.Invalid` error when reporting a validation failure 

424 caused by a value exceeding the maximum. If ``max_err`` is 

425 specified, it must be a string. The string may contain the 

426 replacement targets ``${max}`` and ``${val}``, representing the 

427 maximum value and the provided value respectively. If it is not 

428 provided, it defaults to ``'${val} is greater than maximum value 

429 ${max}'``. 

430 """ 

431 

432 _MIN_ERR = _('${val} is less than minimum value ${min}') 

433 _MAX_ERR = _('${val} is greater than maximum value ${max}') 

434 

435 def __init__(self, min=None, max=None, min_err=_MIN_ERR, max_err=_MAX_ERR): 

436 self.min = min 

437 self.max = max 

438 self.min_err = min_err 

439 self.max_err = max_err 

440 

441 def __call__(self, node, value): 

442 if self.min is not None: 

443 if value < self.min: 

444 min_err = _( 

445 self.min_err, mapping={'val': value, 'min': self.min} 

446 ) 

447 raise Invalid(node, min_err) 

448 

449 if self.max is not None: 

450 if value > self.max: 

451 max_err = _( 

452 self.max_err, mapping={'val': value, 'max': self.max} 

453 ) 

454 raise Invalid(node, max_err) 

455 

456 

457class Length(object): 

458 """Validator which succeeds if the value passed to it has a 

459 length between a minimum and maximum, expressed in the 

460 optional ``min`` and ``max`` arguments. 

461 The value can be any sequence, most often a string. 

462 

463 If ``min`` is not specified, or is specified as ``None``, 

464 no lower bound exists. If ``max`` is not specified, or 

465 is specified as ``None``, no upper bound exists. 

466 

467 The default error messages are "Shorter than minimum length ${min}" 

468 and "Longer than maximum length ${max}". These can be customized: 

469 

470 ``min_err`` is used to form the ``msg`` of the 

471 :exc:`colander.Invalid` error when reporting a validation failure 

472 caused by a value not meeting the minimum length. If ``min_err`` is 

473 specified, it must be a string. The string may contain the 

474 replacement target ``${min}``. 

475 

476 ``max_err`` is used to form the ``msg`` of the 

477 :exc:`colander.Invalid` error when reporting a validation failure 

478 caused by a value exceeding the maximum length. If ``max_err`` is 

479 specified, it must be a string. The string may contain the 

480 replacement target ``${max}``. 

481 """ 

482 

483 _MIN_ERR = _('Shorter than minimum length ${min}') 

484 _MAX_ERR = _('Longer than maximum length ${max}') 

485 

486 def __init__(self, min=None, max=None, min_err=_MIN_ERR, max_err=_MAX_ERR): 

487 self.min = min 

488 self.max = max 

489 self.min_err = min_err 

490 self.max_err = max_err 

491 

492 def __call__(self, node, value): 

493 if self.min is not None: 

494 if len(value) < self.min: 

495 min_err = _(self.min_err, mapping={'min': self.min}) 

496 raise Invalid(node, min_err) 

497 if self.max is not None: 

498 if len(value) > self.max: 

499 max_err = _(self.max_err, mapping={'max': self.max}) 

500 raise Invalid(node, max_err) 

501 

502 

503class OneOf(object): 

504 """ Validator which succeeds if the value passed to it is one of 

505 a fixed set of values """ 

506 

507 def __init__(self, choices): 

508 self.choices = choices 

509 

510 def __call__(self, node, value): 

511 if value not in self.choices: 

512 choices = ', '.join(['%s' % x for x in self.choices]) 

513 err = _( 

514 '"${val}" is not one of ${choices}', 

515 mapping={'val': value, 'choices': choices}, 

516 ) 

517 raise Invalid(node, err) 

518 

519 

520class NoneOf(object): 

521 """ Validator which succeeds if the value passed to it is none of a 

522 fixed set of values. 

523 

524 ``msg_err`` is used to form the ``msg`` of the :exc:`colander.Invalid` 

525 error when reporting a validation failure. If ``msg_err`` is specified, 

526 it must be a string. The string may contain the replacement targets 

527 ``${choices}`` and ``${val}``, representing the set of forbidden values 

528 and the provided value respectively. 

529 """ 

530 

531 _MSG_ERR = _('"${val}" must not be one of ${choices}') 

532 

533 def __init__(self, choices, msg_err=_MSG_ERR): 

534 self.forbidden = choices 

535 self.msg_err = msg_err 

536 

537 def __call__(self, node, value): 

538 if value not in self.forbidden: 

539 return 

540 

541 choices = ', '.join(['%s' % x for x in self.forbidden]) 

542 err = _(self.msg_err, mapping={'val': value, 'choices': choices}) 

543 

544 raise Invalid(node, err) 

545 

546 

547class ContainsOnly(object): 

548 """ Validator which succeeds if the value passed to is a sequence and each 

549 element in the sequence is also in the sequence passed as ``choices``. 

550 This validator is useful when attached to a schemanode with, e.g. a 

551 :class:`colander.Set` or another sequencetype. 

552 """ 

553 

554 err_template = _('One or more of the choices you made was not acceptable') 

555 

556 def __init__(self, choices): 

557 self.choices = choices 

558 

559 def __call__(self, node, value): 

560 if not set(value).issubset(self.choices): 

561 err = _( 

562 self.err_template, 

563 mapping={'val': value, 'choices': self.choices}, 

564 ) 

565 raise Invalid(node, err) 

566 

567 

568def luhnok(node, value): 

569 """ Validator which checks to make sure that the value passes a luhn 

570 mod-10 checksum (credit cards). ``value`` must be a string, not an 

571 integer.""" 

572 try: 

573 checksum = _luhnok(value) 

574 except ValueError: 

575 raise Invalid( 

576 node, 

577 _( 

578 '"${val}" is not a valid credit card number', 

579 mapping={'val': value}, 

580 ), 

581 ) 

582 

583 if (checksum % 10) != 0: 

584 raise Invalid( 

585 node, 

586 _( 

587 '"${val}" is not a valid credit card number', 

588 mapping={'val': value}, 

589 ), 

590 ) 

591 

592 

593def _luhnok(value): 

594 checksum = 0 

595 num_digits = len(value) 

596 oddeven = num_digits & 1 

597 

598 for count in range(0, num_digits): 

599 digit = int(value[count]) 

600 

601 if not ((count & 1) ^ oddeven): 

602 digit *= 2 

603 if digit > 9: 

604 digit -= 9 

605 

606 checksum += digit 

607 return checksum 

608 

609 

610# Gingerly lifted from Django 1.3.x: 

611# https://github.com/django/django/blob/stable/1.3.x/django/core/validators.py#L45 

612# <3 y'all! 

613URL_REGEX = ( 

614 # {http,ftp}s:// (not required) 

615 r'^((?:http|ftp)s?://)?' 

616 # Domain 

617 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+' 

618 r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' 

619 # Localhost 

620 r'localhost|' 

621 # IPv6 address 

622 r'\[[a-f0-9:]+\]|' 

623 # IPv4 address 

624 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' 

625 # Optional port 

626 r'(?::\d+)?' 

627 # Path 

628 r'(?:/?|[/?]\S+)$' 

629) 

630 

631url = Regex(URL_REGEX, msg=_('Must be a URL'), flags=re.IGNORECASE) 

632 

633 

634URI_REGEX = ( 

635 # file:// (required) 

636 r'^file://' 

637 # Path 

638 r'(?:/|[/?]\S+)$' 

639) 

640 

641file_uri = Regex( 

642 URI_REGEX, msg=_('Must be a file:// URI scheme'), flags=re.IGNORECASE 

643) 

644 

645UUID_REGEX = ( 

646 r'^(?:urn:uuid:)?\{?[a-f0-9]{8}(?:-?[a-f0-9]{4}){3}-?[a-f0-9]{12}\}?$' 

647) 

648uuid = Regex(UUID_REGEX, _('Invalid UUID string'), re.IGNORECASE) 

649 

650 

651class SchemaType(object): 

652 """ Base class for all schema types """ 

653 

654 def flatten(self, node, appstruct, prefix='', listitem=False): 

655 result = {} 

656 if listitem: 

657 selfname = prefix 

658 else: 

659 selfname = '%s%s' % (prefix, node.name) 

660 result[selfname.rstrip('.')] = appstruct 

661 return result 

662 

663 def unflatten(self, node, paths, fstruct): 

664 name = node.name 

665 assert paths == [name], "paths should be [name] for leaf nodes." 

666 return fstruct[name] 

667 

668 def set_value(self, node, appstruct, path, value): 

669 raise AssertionError("Can't call 'set_value' on a leaf node.") 

670 

671 def get_value(self, node, appstruct, path): 

672 raise AssertionError("Can't call 'get_value' on a leaf node.") 

673 

674 def cstruct_children(self, node, cstruct): 

675 return [] 

676 

677 

678class Mapping(SchemaType): 

679 """ A type which represents a mapping of names to nodes. 

680 

681 The subnodes of the :class:`colander.SchemaNode` that wraps 

682 this type imply the named keys and values in the mapping. 

683 

684 The constructor of this type accepts one extra optional keyword 

685 argument that other types do not: ``unknown``. An attribute of 

686 the same name can be set on a type instance to control the 

687 behavior after construction. 

688 

689 unknown 

690 ``unknown`` controls the behavior of this type when an unknown 

691 key is encountered in the cstruct passed to the 

692 ``deserialize`` method of this instance. All the potential 

693 values of ``unknown`` are strings. They are: 

694 

695 - ``ignore`` means that keys that are not present in the schema 

696 associated with this type will be ignored during 

697 deserialization. 

698 

699 - ``raise`` will cause a :exc:`colander.Invalid` exception to 

700 be raised when unknown keys are present in the cstruct 

701 during deserialization. 

702 

703 - ``preserve`` will preserve the 'raw' unknown keys and values 

704 in the appstruct returned by deserialization. 

705 

706 Default: ``ignore``. 

707 

708 Special behavior is exhibited when a subvalue of a mapping is 

709 present in the schema but is missing from the mapping passed to 

710 either the ``serialize`` or ``deserialize`` method of this class. 

711 In this case, the :attr:`colander.null` value will be passed to 

712 the ``serialize`` or ``deserialize`` method of the schema node 

713 representing the subvalue of the mapping respectively. During 

714 serialization, this will result in the behavior described in 

715 :ref:`serializing_null` for the subnode. During deserialization, 

716 this will result in the behavior described in 

717 :ref:`deserializing_null` for the subnode. 

718 

719 If the :attr:`colander.null` value is passed to the serialize 

720 method of this class, a dictionary will be returned, where each of 

721 the values in the returned dictionary is the serialized 

722 representation of the null value for its type. 

723 """ 

724 

725 def __init__(self, unknown='ignore'): 

726 self.unknown = unknown 

727 

728 def _set_unknown(self, value): 

729 if value not in ('ignore', 'raise', 'preserve'): 

730 raise ValueError( 

731 'unknown attribute must be one of "ignore", "raise", ' 

732 'or "preserve"' 

733 ) 

734 self._unknown = value 

735 

736 def _get_unknown(self): 

737 return self._unknown 

738 

739 unknown = property(_get_unknown, _set_unknown) 

740 

741 def _validate(self, node, value): 

742 try: 

743 if hasattr(value, 'items'): 

744 return dict(value) 

745 else: 

746 raise TypeError('Does not implement dict-like functionality.') 

747 except Exception as e: 

748 raise Invalid( 

749 node, 

750 _( 

751 '"${val}" is not a mapping type: ${err}', 

752 mapping={'val': value, 'err': e}, 

753 ), 

754 ) 

755 

756 def cstruct_children(self, node, cstruct): 

757 if cstruct is null: 

758 value = {} 

759 else: 

760 value = self._validate(node, cstruct) 

761 children = [] 

762 for subnode in node.children: 

763 name = subnode.name 

764 subval = value.get(name, _marker) 

765 if subval is _marker: 

766 subval = subnode.serialize(null) 

767 children.append(subval) 

768 return children 

769 

770 def _impl(self, node, value, callback): 

771 value = self._validate(node, value) 

772 

773 error = None 

774 result = {} 

775 

776 for num, subnode in enumerate(node.children): 

777 name = subnode.name 

778 subval = value.pop(name, null) 

779 if subval is drop or (subval is null and subnode.default is drop): 

780 continue 

781 try: 

782 sub_result = callback(subnode, subval) 

783 except Invalid as e: 

784 if error is None: 

785 error = Invalid(node) 

786 error.add(e, num) 

787 else: 

788 if sub_result is drop: 

789 continue 

790 result[name] = sub_result 

791 

792 if self.unknown == 'raise': 

793 if value: 

794 raise UnsupportedFields( 

795 node, 

796 value, 

797 msg=_( 

798 'Unrecognized keys in mapping: "${val}"', 

799 mapping={'val': value}, 

800 ), 

801 ) 

802 

803 elif self.unknown == 'preserve': 

804 result.update(copy.deepcopy(value)) 

805 

806 if error is not None: 

807 raise error 

808 

809 return result 

810 

811 def serialize(self, node, appstruct): 

812 if appstruct is null: 

813 appstruct = {} 

814 

815 def callback(subnode, subappstruct): 

816 return subnode.serialize(subappstruct) 

817 

818 return self._impl(node, appstruct, callback) 

819 

820 def deserialize(self, node, cstruct): 

821 if cstruct is null: 

822 return null 

823 

824 def callback(subnode, subcstruct): 

825 return subnode.deserialize(subcstruct) 

826 

827 return self._impl(node, cstruct, callback) 

828 

829 def flatten(self, node, appstruct, prefix='', listitem=False): 

830 result = {} 

831 if listitem: 

832 selfprefix = prefix 

833 else: 

834 if node.name: 

835 selfprefix = '%s%s.' % (prefix, node.name) 

836 else: 

837 selfprefix = prefix 

838 

839 for subnode in node.children: 

840 name = subnode.name 

841 substruct = appstruct.get(name, null) 

842 result.update( 

843 subnode.typ.flatten(subnode, substruct, prefix=selfprefix) 

844 ) 

845 return result 

846 

847 def unflatten(self, node, paths, fstruct): 

848 return _unflatten_mapping(node, paths, fstruct) 

849 

850 def set_value(self, node, appstruct, path, value): 

851 if '.' in path: 

852 next_name, rest = path.split('.', 1) 

853 next_node = node[next_name] 

854 next_appstruct = appstruct[next_name] 

855 appstruct[next_name] = next_node.typ.set_value( 

856 next_node, next_appstruct, rest, value 

857 ) 

858 else: 

859 appstruct[path] = value 

860 return appstruct 

861 

862 def get_value(self, node, appstruct, path): 

863 if '.' in path: 

864 name, rest = path.split('.', 1) 

865 next_node = node[name] 

866 return next_node.typ.get_value(next_node, appstruct[name], rest) 

867 return appstruct[path] 

868 

869 

870class Positional(object): 

871 """ 

872 Marker abstract base class meaning 'this type has children which 

873 should be addressed by position instead of name' (e.g. via seq[0], 

874 but never seq['name']). This is consulted by Invalid.asdict when 

875 creating a dictionary representation of an error tree. 

876 """ 

877 

878 

879class Tuple(Positional, SchemaType): 

880 """ A type which represents a fixed-length sequence of nodes. 

881 

882 The subnodes of the :class:`colander.SchemaNode` that wraps 

883 this type imply the positional elements of the tuple in the order 

884 they are added. 

885 

886 This type is willing to serialize and deserialized iterables that, 

887 when converted to a tuple, have the same number of elements as the 

888 number of the associated node's subnodes. 

889 

890 If the :attr:`colander.null` value is passed to the serialize 

891 method of this class, the :attr:`colander.null` value will be 

892 returned. 

893 """ 

894 

895 def _validate(self, node, value): 

896 if not hasattr(value, '__iter__'): 

897 raise Invalid( 

898 node, _('"${val}" is not iterable', mapping={'val': value}) 

899 ) 

900 

901 valuelen, nodelen = len(value), len(node.children) 

902 

903 if valuelen != nodelen: 

904 raise Invalid( 

905 node, 

906 _( 

907 '"${val}" has an incorrect number of elements ' 

908 '(expected ${exp}, was ${was})', 

909 mapping={'val': value, 'exp': nodelen, 'was': valuelen}, 

910 ), 

911 ) 

912 

913 return list(value) 

914 

915 def cstruct_children(self, node, cstruct): 

916 childlen = len(node.children) 

917 if cstruct is null: 

918 cstruct = [] 

919 structlen = len(cstruct) 

920 if structlen < childlen: 

921 missing_children = node.children[structlen:] 

922 cstruct = list(cstruct) 

923 for child in missing_children: 

924 cstruct.append(child.serialize(null)) 

925 elif structlen > childlen: 

926 cstruct = cstruct[:childlen] 

927 else: 

928 cstruct = list(cstruct) 

929 return cstruct 

930 

931 def _impl(self, node, value, callback): 

932 value = self._validate(node, value) 

933 error = None 

934 result = [] 

935 

936 for num, subnode in enumerate(node.children): 

937 subval = value[num] 

938 try: 

939 result.append(callback(subnode, subval)) 

940 except Invalid as e: 

941 if error is None: 

942 error = Invalid(node) 

943 error.add(e, num) 

944 

945 if error is not None: 

946 raise error 

947 

948 return tuple(result) 

949 

950 def serialize(self, node, appstruct): 

951 if appstruct is null: 

952 return null 

953 

954 def callback(subnode, subappstruct): 

955 return subnode.serialize(subappstruct) 

956 

957 return self._impl(node, appstruct, callback) 

958 

959 def deserialize(self, node, cstruct): 

960 if cstruct is null: 

961 return null 

962 

963 def callback(subnode, subval): 

964 return subnode.deserialize(subval) 

965 

966 return self._impl(node, cstruct, callback) 

967 

968 def flatten(self, node, appstruct, prefix='', listitem=False): 

969 result = {} 

970 if listitem: 

971 selfprefix = prefix 

972 else: 

973 selfprefix = '%s%s.' % (prefix, node.name) 

974 

975 for num, subnode in enumerate(node.children): 

976 substruct = appstruct[num] 

977 result.update( 

978 subnode.typ.flatten(subnode, substruct, prefix=selfprefix) 

979 ) 

980 return result 

981 

982 def unflatten(self, node, paths, fstruct): 

983 mapstruct = _unflatten_mapping(node, paths, fstruct) 

984 appstruct = [] 

985 for subnode in node.children: 

986 appstruct.append(mapstruct[subnode.name]) 

987 return tuple(appstruct) 

988 

989 def set_value(self, node, appstruct, path, value): 

990 appstruct = list(appstruct) 

991 if '.' in path: 

992 next_name, rest = path.split('.', 1) 

993 else: 

994 next_name, rest = path, None 

995 for index, next_node in enumerate(node.children): 

996 if next_node.name == next_name: 

997 break 

998 else: 

999 raise KeyError(next_name) 

1000 if rest is not None: 

1001 next_appstruct = appstruct[index] 

1002 appstruct[index] = next_node.typ.set_value( 

1003 next_node, next_appstruct, rest, value 

1004 ) 

1005 else: 

1006 appstruct[index] = value 

1007 return tuple(appstruct) 

1008 

1009 def get_value(self, node, appstruct, path): 

1010 if '.' in path: 

1011 name, rest = path.split('.', 1) 

1012 else: 

1013 name, rest = path, None 

1014 for index, next_node in enumerate(node.children): 

1015 if next_node.name == name: 

1016 break 

1017 else: 

1018 raise KeyError(name) 

1019 if rest is not None: 

1020 return next_node.typ.get_value(next_node, appstruct[index], rest) 

1021 return appstruct[index] 

1022 

1023 

1024class Set(SchemaType): 

1025 """ A type representing a non-overlapping set of items. 

1026 Deserializes an iterable to a ``set`` object. 

1027 

1028 If the :attr:`colander.null` value is passed to the serialize 

1029 method of this class, the :attr:`colander.null` value will be 

1030 returned. 

1031 

1032 .. versionadded: 1.0a1 

1033 

1034 """ 

1035 

1036 def serialize(self, node, appstruct): 

1037 if appstruct is null: 

1038 return null 

1039 

1040 return appstruct 

1041 

1042 def deserialize(self, node, cstruct): 

1043 if cstruct is null: 

1044 return null 

1045 

1046 if not is_nonstr_iter(cstruct): 

1047 raise Invalid( 

1048 node, 

1049 _('${cstruct} is not iterable', mapping={'cstruct': cstruct}), 

1050 ) 

1051 

1052 return set(cstruct) 

1053 

1054 

1055class List(SchemaType): 

1056 """ Type representing an ordered sequence of items. 

1057 

1058 Desrializes an iterable to a ``list`` object. 

1059 

1060 If the :attr:`colander.null` value is passed to the serialize 

1061 method of this class, the :attr:`colander.null` value will be 

1062 returned. 

1063 

1064 .. versionadded: 1.0a6 

1065 """ 

1066 

1067 def serialize(self, node, appstruct): 

1068 if appstruct is null: 

1069 return null 

1070 

1071 return appstruct 

1072 

1073 def deserialize(self, node, cstruct): 

1074 if cstruct is null: 

1075 return null 

1076 

1077 if not is_nonstr_iter(cstruct): 

1078 raise Invalid( 

1079 node, 

1080 _('${cstruct} is not iterable', mapping={'cstruct': cstruct}), 

1081 ) 

1082 

1083 return list(cstruct) 

1084 

1085 

1086class SequenceItems(list): 

1087 """ 

1088 List marker subclass for use by Sequence.cstruct_children, which indicates 

1089 to a caller of that method that the result is from a sequence type. 

1090 Usually these values need to be treated specially, because all of the 

1091 children of a Sequence are not present in a schema. 

1092 """ 

1093 

1094 

1095class Sequence(Positional, SchemaType): 

1096 """ 

1097 A type which represents a variable-length sequence of nodes, 

1098 all of which must be of the same type. 

1099 

1100 The type of the first subnode of the 

1101 :class:`colander.SchemaNode` that wraps this type is considered the 

1102 sequence type. 

1103 

1104 The optional ``accept_scalar`` argument to this type's constructor 

1105 indicates what should happen if the value found during serialization or 

1106 deserialization does not have an ``__iter__`` method or is a 

1107 mapping type. 

1108 

1109 If ``accept_scalar`` is ``True`` and the value does not have an 

1110 ``__iter__`` method or is a mapping type, the value will be turned 

1111 into a single element list. 

1112 

1113 If ``accept_scalar`` is ``False`` and the value does not have an 

1114 ``__iter__`` method or is a mapping type, an 

1115 :exc:`colander.Invalid` error will be raised during serialization 

1116 and deserialization. 

1117 

1118 The default value of ``accept_scalar`` is ``False``. 

1119 

1120 If the :attr:`colander.null` value is passed to the serialize 

1121 method of this class, the :attr:`colander.null` value is returned. 

1122 """ 

1123 

1124 def __init__(self, accept_scalar=False): 

1125 self.accept_scalar = accept_scalar 

1126 

1127 def _validate(self, node, value, accept_scalar): 

1128 if ( 

1129 hasattr(value, '__iter__') 

1130 and not hasattr(value, 'get') 

1131 and not isinstance(value, string_types) 

1132 ): 

1133 return list(value) 

1134 if accept_scalar: 

1135 return [value] 

1136 else: 

1137 raise Invalid( 

1138 node, _('"${val}" is not iterable', mapping={'val': value}) 

1139 ) 

1140 

1141 def cstruct_children(self, node, cstruct): 

1142 if cstruct is null: 

1143 return SequenceItems([]) 

1144 return SequenceItems(cstruct) 

1145 

1146 def _impl(self, node, value, callback, accept_scalar): 

1147 if accept_scalar is None: 

1148 accept_scalar = self.accept_scalar 

1149 

1150 value = self._validate(node, value, accept_scalar) 

1151 

1152 error = None 

1153 result = [] 

1154 

1155 subnode = node.children[0] 

1156 for num, subval in enumerate(value): 

1157 if subval is drop or (subval is null and subnode.default is drop): 

1158 continue 

1159 try: 

1160 sub_result = callback(subnode, subval) 

1161 except Invalid as e: 

1162 if error is None: 

1163 error = Invalid(node) 

1164 error.add(e, num) 

1165 else: 

1166 if sub_result is drop: 

1167 continue 

1168 result.append(sub_result) 

1169 

1170 if error is not None: 

1171 raise error 

1172 

1173 return result 

1174 

1175 def serialize(self, node, appstruct, accept_scalar=None): 

1176 """ 

1177 Along with the normal ``node`` and ``appstruct`` arguments, 

1178 this method accepts an additional optional keyword argument: 

1179 ``accept_scalar``. This keyword argument can be used to 

1180 override the constructor value of the same name. 

1181 

1182 If ``accept_scalar`` is ``True`` and the ``appstruct`` does 

1183 not have an ``__iter__`` method or is a mapping type, the 

1184 value will be turned into a single element list. 

1185 

1186 If ``accept_scalar`` is ``False`` and the ``appstruct`` does 

1187 not have an ``__iter__`` method or is a mapping type, an 

1188 :exc:`colander.Invalid` error will be raised during 

1189 serialization and deserialization. 

1190 

1191 The default of ``accept_scalar`` is ``None``, which means 

1192 respect the default ``accept_scalar`` value attached to this 

1193 instance via its constructor. 

1194 """ 

1195 if appstruct is null: 

1196 return null 

1197 

1198 def callback(subnode, subappstruct): 

1199 return subnode.serialize(subappstruct) 

1200 

1201 return self._impl(node, appstruct, callback, accept_scalar) 

1202 

1203 def deserialize(self, node, cstruct, accept_scalar=None): 

1204 """ 

1205 Along with the normal ``node`` and ``cstruct`` arguments, this 

1206 method accepts an additional optional keyword argument: 

1207 ``accept_scalar``. This keyword argument can be used to 

1208 override the constructor value of the same name. 

1209 

1210 If ``accept_scalar`` is ``True`` and the ``cstruct`` does not 

1211 have an ``__iter__`` method or is a mapping type, the value 

1212 will be turned into a single element list. 

1213 

1214 If ``accept_scalar`` is ``False`` and the ``cstruct`` does not have an 

1215 ``__iter__`` method or is a mapping type, an 

1216 :exc:`colander.Invalid` error will be raised during serialization 

1217 and deserialization. 

1218 

1219 The default of ``accept_scalar`` is ``None``, which means 

1220 respect the default ``accept_scalar`` value attached to this 

1221 instance via its constructor. 

1222 """ 

1223 if cstruct is null: 

1224 return null 

1225 

1226 def callback(subnode, subcstruct): 

1227 return subnode.deserialize(subcstruct) 

1228 

1229 return self._impl(node, cstruct, callback, accept_scalar) 

1230 

1231 def flatten(self, node, appstruct, prefix='', listitem=False): 

1232 result = {} 

1233 if listitem: 

1234 selfprefix = prefix 

1235 else: 

1236 selfprefix = '%s%s.' % (prefix, node.name) 

1237 

1238 childnode = node.children[0] 

1239 

1240 for num, subval in enumerate(appstruct): 

1241 subname = '%s%s' % (selfprefix, num) 

1242 subprefix = subname + '.' 

1243 result.update( 

1244 childnode.typ.flatten( 

1245 childnode, subval, prefix=subprefix, listitem=True 

1246 ) 

1247 ) 

1248 

1249 return result 

1250 

1251 def unflatten(self, node, paths, fstruct): 

1252 only_child = node.children[0] 

1253 child_name = only_child.name 

1254 

1255 def get_child(name): 

1256 return only_child 

1257 

1258 def rewrite_subpath(subpath): 

1259 if '.' in subpath: 

1260 suffix = subpath.split('.', 1)[1] 

1261 return '%s.%s' % (child_name, suffix) 

1262 return child_name 

1263 

1264 mapstruct = _unflatten_mapping( 

1265 node, paths, fstruct, get_child, rewrite_subpath 

1266 ) 

1267 return [mapstruct[str(index)] for index in xrange(len(mapstruct))] 

1268 

1269 def set_value(self, node, appstruct, path, value): 

1270 if '.' in path: 

1271 next_name, rest = path.split('.', 1) 

1272 index = int(next_name) 

1273 next_node = node.children[0] 

1274 next_appstruct = appstruct[index] 

1275 appstruct[index] = next_node.typ.set_value( 

1276 next_node, next_appstruct, rest, value 

1277 ) 

1278 else: 

1279 index = int(path) 

1280 appstruct[index] = value 

1281 return appstruct 

1282 

1283 def get_value(self, node, appstruct, path): 

1284 if '.' in path: 

1285 name, rest = path.split('.', 1) 

1286 index = int(name) 

1287 next_node = node.children[0] 

1288 return next_node.typ.get_value(next_node, appstruct[index], rest) 

1289 return appstruct[int(path)] 

1290 

1291 

1292Seq = Sequence 

1293 

1294 

1295class String(SchemaType): 

1296 """ A type representing a Unicode string. 

1297 

1298 This type constructor accepts two arguments: 

1299 

1300 ``encoding`` 

1301 Represents the encoding which should be applied to value 

1302 serialization and deserialization, for example ``utf-8``. If 

1303 ``encoding`` is passed as ``None``, the ``serialize`` method of 

1304 this type will not do any special encoding of the appstruct it is 

1305 provided, nor will the ``deserialize`` method of this type do 

1306 any special decoding of the cstruct it is provided; inputs and 

1307 outputs will be assumed to be Unicode. ``encoding`` defaults 

1308 to ``None``. 

1309 

1310 If ``encoding`` is ``None``: 

1311 

1312 - A Unicode input value to ``serialize`` is returned untouched. 

1313 

1314 - A non-Unicode input value to ``serialize`` is run through the 

1315 ``unicode()`` function without an ``encoding`` parameter 

1316 (``unicode(value)``) and the result is returned. 

1317 

1318 - A Unicode input value to ``deserialize`` is returned untouched. 

1319 

1320 - A non-Unicode input value to ``deserialize`` is run through the 

1321 ``unicode()`` function without an ``encoding`` parameter 

1322 (``unicode(value)``) and the result is returned. 

1323 

1324 If ``encoding`` is not ``None``: 

1325 

1326 - A Unicode input value to ``serialize`` is run through the 

1327 ``unicode`` function with the encoding parameter 

1328 (``unicode(value, encoding)``) and the result (a ``str`` 

1329 object) is returned. 

1330 

1331 - A non-Unicode input value to ``serialize`` is converted to a 

1332 Unicode using the encoding (``unicode(value, encoding)``); 

1333 subsequently the Unicode object is re-encoded to a ``str`` 

1334 object using the encoding and returned. 

1335 

1336 - A Unicode input value to ``deserialize`` is returned 

1337 untouched. 

1338 

1339 - A non-Unicode input value to ``deserialize`` is converted to 

1340 a ``str`` object using ``str(value``). The resulting str 

1341 value is converted to Unicode using the encoding 

1342 (``unicode(value, encoding)``) and the result is returned. 

1343 

1344 A corollary: If a string (as opposed to a unicode object) is 

1345 provided as a value to either the serialize or deserialize 

1346 method of this type, and the type also has an non-None 

1347 ``encoding``, the string must be encoded with the type's 

1348 encoding. If this is not true, an :exc:`colander.Invalid` 

1349 error will result. 

1350 

1351 ``allow_empty`` 

1352 Boolean, if True allows deserialization of an empty string. If 

1353 False (default), empty strings will deserialize to 

1354 :attr:`colander.null` 

1355 

1356 The subnodes of the :class:`colander.SchemaNode` that wraps 

1357 this type are ignored. 

1358 """ 

1359 

1360 def __init__(self, encoding=None, allow_empty=False): 

1361 self.encoding = encoding 

1362 self.allow_empty = allow_empty 

1363 

1364 def serialize(self, node, appstruct): 

1365 if appstruct is null: 

1366 return null 

1367 

1368 try: 

1369 if isinstance(appstruct, (text_type, bytes)): 

1370 encoding = self.encoding 

1371 if encoding: 

1372 result = text_(appstruct, encoding).encode(encoding) 

1373 else: 

1374 result = text_type(appstruct) 

1375 else: 

1376 result = text_type(appstruct) 

1377 if self.encoding: 

1378 result = result.encode(self.encoding) 

1379 return result 

1380 except Exception as e: 

1381 raise Invalid( 

1382 node, 

1383 _( 

1384 '${val} cannot be serialized: ${err}', 

1385 mapping={'val': appstruct, 'err': e}, 

1386 ), 

1387 ) 

1388 

1389 def deserialize(self, node, cstruct): 

1390 if cstruct == '' and self.allow_empty: 

1391 return text_type('') 

1392 

1393 if not cstruct: 

1394 return null 

1395 

1396 try: 

1397 result = cstruct 

1398 if isinstance(result, (text_type, bytes)): 

1399 if self.encoding: 

1400 result = text_(cstruct, self.encoding) 

1401 else: 

1402 result = text_type(cstruct) 

1403 else: 

1404 raise Invalid(node) 

1405 except Exception as e: 

1406 raise Invalid( 

1407 node, 

1408 _( 

1409 '${val} is not a string: ${err}', 

1410 mapping={'val': cstruct, 'err': e}, 

1411 ), 

1412 ) 

1413 

1414 return result 

1415 

1416 

1417Str = String 

1418 

1419 

1420class Number(SchemaType): 

1421 """ Abstract base class for float, int, decimal """ 

1422 

1423 num = None 

1424 

1425 def serialize(self, node, appstruct): 

1426 if appstruct in (null, None): 

1427 return null 

1428 

1429 try: 

1430 return str(self.num(appstruct)) 

1431 except Exception: 

1432 raise Invalid( 

1433 node, _('"${val}" is not a number', mapping={'val': appstruct}) 

1434 ) 

1435 

1436 def deserialize(self, node, cstruct): 

1437 if cstruct != 0 and not cstruct: 

1438 return null 

1439 

1440 try: 

1441 return self.num(cstruct) 

1442 except Exception: 

1443 raise Invalid( 

1444 node, _('"${val}" is not a number', mapping={'val': cstruct}) 

1445 ) 

1446 

1447 

1448class Integer(Number): 

1449 """ A type representing an integer. 

1450 

1451 If the :attr:`colander.null` value is passed to the serialize 

1452 method of this class, the :attr:`colander.null` value will be 

1453 returned. 

1454 

1455 The Integer constructor takes an optional argument ``strict``, which if 

1456 enabled will verify that the number passed to serialize/deserialize is an 

1457 integer, and not a float that would get truncated. 

1458 

1459 The subnodes of the :class:`colander.SchemaNode` that wraps 

1460 this type are ignored. 

1461 """ 

1462 

1463 num = int 

1464 

1465 def __init__(self, strict=False): 

1466 if strict: 

1467 

1468 def _strict_int(val): 

1469 if not float(val).is_integer(): 

1470 raise ValueError("Value is not an Integer") 

1471 return int(val) 

1472 

1473 self.num = _strict_int 

1474 

1475 super(Integer, self).__init__() 

1476 

1477 

1478Int = Integer 

1479 

1480 

1481class Float(Number): 

1482 """ A type representing a float. 

1483 

1484 If the :attr:`colander.null` value is passed to the serialize 

1485 method of this class, the :attr:`colander.null` value will be 

1486 returned. 

1487 

1488 The subnodes of the :class:`colander.SchemaNode` that wraps 

1489 this type are ignored. 

1490 """ 

1491 

1492 num = float 

1493 

1494 

1495class Decimal(Number): 

1496 """ 

1497 A type representing a decimal floating point. Deserialization returns an 

1498 instance of the Python ``decimal.Decimal`` type. 

1499 

1500 If the :attr:`colander.null` value is passed to the serialize 

1501 method of this class, the :attr:`colander.null` value will be 

1502 returned. 

1503 

1504 The Decimal constructor takes three optional arguments, ``quant``, 

1505 ``rounding`` and ``normalize``. If supplied, ``quant`` should be a string, 

1506 (e.g. ``1.00``). If supplied, ``rounding`` should be one of the Python 

1507 ``decimal`` module rounding options (e.g. ``decimal.ROUND_UP``, 

1508 ``decimal.ROUND_DOWN``, etc). The serialized and deserialized result 

1509 will be quantized and rounded via 

1510 ``result.quantize(decimal.Decimal(quant), rounding)``. ``rounding`` is 

1511 ignored if ``quant`` is not supplied. If ``normalize`` is ``True``, 

1512 the serialized and deserialized result will be normalized by stripping 

1513 the rightmost trailing zeros. 

1514 

1515 The subnodes of the :class:`colander.SchemaNode` that wraps 

1516 this type are ignored. 

1517 """ 

1518 

1519 def __init__(self, quant=None, rounding=None, normalize=False): 

1520 if quant is None: 

1521 self.quant = None 

1522 else: 

1523 self.quant = decimal.Decimal(quant) 

1524 self.rounding = rounding 

1525 self.normalize = normalize 

1526 

1527 def num(self, val): 

1528 result = decimal.Decimal(str(val)) 

1529 if self.quant is not None: 

1530 if self.rounding is None: 

1531 result = result.quantize(self.quant) 

1532 else: 

1533 result = result.quantize(self.quant, self.rounding) 

1534 if self.normalize: 

1535 result = result.normalize() 

1536 return result 

1537 

1538 

1539class Money(Decimal): 

1540 """ A type representing a money value with two digit precision. 

1541 Deserialization returns an instance of the Python ``decimal.Decimal`` 

1542 type (quantized to two decimal places, rounded up). 

1543 

1544 If the :attr:`colander.null` value is passed to the serialize 

1545 method of this class, the :attr:`colander.null` value will be 

1546 returned. 

1547 

1548 The subnodes of the :class:`colander.SchemaNode` that wraps 

1549 this type are ignored. 

1550 """ 

1551 

1552 def __init__(self): 

1553 super(Money, self).__init__(decimal.Decimal('.01'), decimal.ROUND_UP) 

1554 

1555 

1556class Boolean(SchemaType): 

1557 """ A type representing a boolean object. 

1558 

1559 The constructor accepts these keyword arguments: 

1560 

1561 - ``false_choices``: The set of strings representing a ``False`` 

1562 value on deserialization. 

1563 

1564 - ``true_choices``: The set of strings representing a ``True`` 

1565 value on deserialization. 

1566 

1567 - ``false_val``: The value returned on serialization of a False 

1568 value. 

1569 

1570 - ``true_val``: The value returned on serialization of a True 

1571 value. 

1572 

1573 During deserialization, a value contained in :attr:`false_choices`, 

1574 will be considered ``False``. 

1575 

1576 The behaviour for values not contained in :attr:`false_choices` 

1577 depends on :attr:`true_choices`: if it's empty, any value is considered 

1578 ``True``; otherwise, only values contained in :attr:`true_choices` 

1579 are considered ``True``, and an Invalid exception would be raised 

1580 for values outside of both :attr:`false_choices` and :attr:`true_choices`. 

1581 

1582 Serialization will produce :attr:`true_val` or :attr:`false_val` 

1583 based on the value. 

1584 

1585 If the :attr:`colander.null` value is passed to the serialize 

1586 method of this class, the :attr:`colander.null` value will be 

1587 returned. 

1588 

1589 The subnodes of the :class:`colander.SchemaNode` that wraps 

1590 this type are ignored. 

1591 """ 

1592 

1593 def __init__( 

1594 self, 

1595 false_choices=('false', '0'), 

1596 true_choices=(), 

1597 false_val='false', 

1598 true_val='true', 

1599 ): 

1600 

1601 self.false_choices = false_choices 

1602 self.true_choices = true_choices 

1603 self.false_val = false_val 

1604 self.true_val = true_val 

1605 

1606 self.true_reprs = ', '.join([repr(c) for c in self.true_choices]) 

1607 self.false_reprs = ', '.join([repr(c) for c in self.false_choices]) 

1608 

1609 def serialize(self, node, appstruct): 

1610 if appstruct is null: 

1611 return null 

1612 

1613 return appstruct and self.true_val or self.false_val 

1614 

1615 def deserialize(self, node, cstruct): 

1616 if cstruct is null: 

1617 return null 

1618 

1619 try: 

1620 result = str(cstruct) 

1621 except Exception: 

1622 raise Invalid( 

1623 node, _('${val} is not a string', mapping={'val': cstruct}) 

1624 ) 

1625 result = result.lower() 

1626 

1627 if result in self.false_choices: 

1628 return False 

1629 elif self.true_choices: 

1630 if result in self.true_choices: 

1631 return True 

1632 else: 

1633 raise Invalid( 

1634 node, 

1635 _( 

1636 '"${val}" is neither in (${false_choices}) ' 

1637 'nor in (${true_choices})', 

1638 mapping={ 

1639 'val': cstruct, 

1640 'false_choices': self.false_reprs, 

1641 'true_choices': self.true_reprs, 

1642 }, 

1643 ), 

1644 ) 

1645 

1646 return True 

1647 

1648 

1649Bool = Boolean 

1650 

1651 

1652class GlobalObject(SchemaType): 

1653 """ A type representing an importable Python object. This type 

1654 serializes 'global' Python objects (objects which can be imported) 

1655 to dotted Python names. 

1656 

1657 Two dotted name styles are supported during deserialization: 

1658 

1659 - ``pkg_resources``-style dotted names where non-module attributes 

1660 of a module are separated from the rest of the path using a ':' 

1661 e.g. ``package.module:attr``. 

1662 

1663 - ``zope.dottedname``-style dotted names where non-module 

1664 attributes of a module are separated from the rest of the path 

1665 using a '.' e.g. ``package.module.attr``. 

1666 

1667 These styles can be used interchangeably. If the serialization 

1668 contains a ``:`` (colon), the ``pkg_resources`` resolution 

1669 mechanism will be chosen, otherwise the ``zope.dottedname`` 

1670 resolution mechanism will be chosen. 

1671 

1672 The constructor accepts a single argument named ``package`` which 

1673 should be a Python module or package object; it is used when 

1674 *relative* dotted names are supplied to the ``deserialize`` 

1675 method. A serialization which has a ``.`` (dot) or ``:`` (colon) 

1676 as its first character is treated as relative. E.g. if 

1677 ``.minidom`` is supplied to ``deserialize``, and the ``package`` 

1678 argument to this type was passed the ``xml`` module object, the 

1679 resulting import would be for ``xml.minidom``. If a relative 

1680 package name is supplied to ``deserialize``, and no ``package`` 

1681 was supplied to the constructor, an :exc:`colander.Invalid` error 

1682 will be raised. 

1683 

1684 If the :attr:`colander.null` value is passed to the serialize 

1685 method of this class, the :attr:`colander.null` value will be 

1686 returned. 

1687 

1688 The subnodes of the :class:`colander.SchemaNode` that wraps 

1689 this type are ignored. 

1690 """ 

1691 

1692 def __init__(self, package): 

1693 self.package = package 

1694 

1695 def _pkg_resources_style(self, node, value): 

1696 """ package.module:attr style """ 

1697 import pkg_resources 

1698 

1699 if value.startswith('.') or value.startswith(':'): 

1700 if not self.package: 

1701 raise Invalid( 

1702 node, 

1703 _( 

1704 'relative name "${val}" irresolveable without package', 

1705 mapping={'val': value}, 

1706 ), 

1707 ) 

1708 if value in ['.', ':']: 

1709 value = self.package.__name__ 

1710 else: 

1711 value = self.package.__name__ + value 

1712 return pkg_resources.EntryPoint.parse('x=%s' % value).load(False) 

1713 

1714 def _zope_dottedname_style(self, node, value): 

1715 """ package.module.attr style """ 

1716 module = self.package and self.package.__name__ or None 

1717 if value == '.': 

1718 if self.package is None: 

1719 raise Invalid( 

1720 node, 

1721 _( 

1722 'relative name "${val}" irresolveable without package', 

1723 mapping={'val': value}, 

1724 ), 

1725 ) 

1726 name = module.split('.') 

1727 else: 

1728 name = value.split('.') 

1729 if not name[0]: 

1730 if module is None: 

1731 raise Invalid( 

1732 node, 

1733 _( 

1734 'relative name "${val}" irresolveable without ' 

1735 'package', 

1736 mapping={'val': value}, 

1737 ), 

1738 ) 

1739 module = module.split('.') 

1740 name.pop(0) 

1741 while not name[0]: 

1742 module.pop() 

1743 name.pop(0) 

1744 name = module + name 

1745 

1746 used = name.pop(0) 

1747 found = __import__(used) 

1748 for n in name: 

1749 used += '.' + n 

1750 try: 

1751 found = getattr(found, n) 

1752 except AttributeError: # pragma: no cover 

1753 __import__(used) 

1754 found = getattr(found, n) 

1755 

1756 return found 

1757 

1758 def serialize(self, node, appstruct): 

1759 if appstruct is null: 

1760 return null 

1761 

1762 try: 

1763 if isinstance(appstruct, types.ModuleType): 

1764 return appstruct.__name__ 

1765 else: 

1766 return '{0.__module__}.{0.__name__}'.format(appstruct) 

1767 

1768 except AttributeError: 

1769 raise Invalid( 

1770 node, _('"${val}" has no __name__', mapping={'val': appstruct}) 

1771 ) 

1772 

1773 def deserialize(self, node, cstruct): 

1774 if not cstruct: 

1775 return null 

1776 

1777 if not isinstance(cstruct, string_types): 

1778 raise Invalid( 

1779 node, _('"${val}" is not a string', mapping={'val': cstruct}) 

1780 ) 

1781 try: 

1782 if ':' in cstruct: 

1783 return self._pkg_resources_style(node, cstruct) 

1784 else: 

1785 return self._zope_dottedname_style(node, cstruct) 

1786 except ImportError: 

1787 raise Invalid( 

1788 node, 

1789 _( 

1790 'The dotted name "${name}" cannot be imported', 

1791 mapping={'name': cstruct}, 

1792 ), 

1793 ) 

1794 

1795 

1796class DateTime(SchemaType): 

1797 """ A type representing a Python ``datetime.datetime`` object. 

1798 

1799 This type serializes python ``datetime.datetime`` objects to a 

1800 `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ string format. 

1801 The format includes the date, the time, and the timezone of the 

1802 datetime. 

1803 

1804 The constructor accepts an argument named ``default_tzinfo`` which 

1805 should be a Python ``tzinfo`` object. If ``default_tzinfo`` is not 

1806 specified the default tzinfo will be equivalent to UTC (Zulu time). 

1807 The ``default_tzinfo`` tzinfo object is used to convert 'naive' 

1808 datetimes to a timezone-aware representation during serialization. 

1809 If ``default_tzinfo`` is explicitly set to ``None`` then no default 

1810 tzinfo will be applied to naive datetimes. 

1811 

1812 You can adjust the error message reported by this class by 

1813 changing its ``err_template`` attribute in a subclass on an 

1814 instance of this class. By default, the ``err_template`` 

1815 attribute is the string ``Invalid date``. This string is used as 

1816 the interpolation subject of a dictionary composed of ``val`` and 

1817 ``err``. ``val`` and ``err`` are the unvalidatable value and the 

1818 exception caused trying to convert the value, respectively. These 

1819 may be used in an overridden err_template as ``${val}`` and 

1820 ``${err}`` respectively as necessary, e.g. ``_('${val} cannot be 

1821 parsed as an iso8601 date: ${err}')``. 

1822 

1823 For convenience, this type is also willing to coerce 

1824 ``datetime.date`` objects to a DateTime ISO string representation 

1825 during serialization. It does so by using midnight of the day as 

1826 the time, and uses the ``default_tzinfo`` to give the 

1827 serialization a timezone. 

1828 

1829 Likewise, for convenience, during deserialization, this type will 

1830 convert ``YYYY-MM-DD`` ISO8601 values to a datetime object. It 

1831 does so by using midnight of the day as the time, and uses the 

1832 ``default_tzinfo`` to give the serialization a timezone. 

1833 

1834 If the :attr:`colander.null` value is passed to the serialize 

1835 method of this class, the :attr:`colander.null` value will be 

1836 returned. 

1837 

1838 The subnodes of the :class:`colander.SchemaNode` that wraps 

1839 this type are ignored. 

1840 """ 

1841 

1842 err_template = _('Invalid date') 

1843 

1844 def __init__(self, default_tzinfo=iso8601.UTC, format=None): 

1845 self.default_tzinfo = default_tzinfo 

1846 self.format = format 

1847 

1848 def serialize(self, node, appstruct): 

1849 if not appstruct: 

1850 return null 

1851 

1852 # cant use isinstance; dt subs date 

1853 if type(appstruct) is datetime.date: 

1854 appstruct = datetime.datetime.combine(appstruct, datetime.time()) 

1855 

1856 if not isinstance(appstruct, datetime.datetime): 

1857 raise Invalid( 

1858 node, 

1859 _( 

1860 '"${val}" is not a datetime object', 

1861 mapping={'val': appstruct}, 

1862 ), 

1863 ) 

1864 

1865 if appstruct.tzinfo is None: 

1866 appstruct = appstruct.replace(tzinfo=self.default_tzinfo) 

1867 if not self.format: 

1868 return appstruct.isoformat() 

1869 else: 

1870 return appstruct.strftime(self.format) 

1871 

1872 def deserialize(self, node, cstruct): 

1873 if not cstruct: 

1874 return null 

1875 

1876 try: 

1877 if self.format: 

1878 result = datetime.datetime.strptime(cstruct, self.format) 

1879 if not result.tzinfo and self.default_tzinfo: 

1880 result = result.replace(tzinfo=self.default_tzinfo) 

1881 else: 

1882 result = iso8601.parse_date( 

1883 cstruct, default_timezone=self.default_tzinfo 

1884 ) 

1885 except (ValueError, iso8601.ParseError) as e: 

1886 raise Invalid( 

1887 node, _(self.err_template, mapping={'val': cstruct, 'err': e}) 

1888 ) 

1889 return result 

1890 

1891 

1892class Date(SchemaType): 

1893 """ A type representing a Python ``datetime.date`` object. 

1894 

1895 This type serializes python ``datetime.date`` objects to a 

1896 `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ string format. 

1897 The format includes the date only. 

1898 

1899 The constructor accepts no arguments. 

1900 

1901 You can adjust the error message reported by this class by 

1902 changing its ``err_template`` attribute in a subclass on an 

1903 instance of this class. By default, the ``err_template`` 

1904 attribute is the string ``Invalid date``. This string is used as 

1905 the interpolation subject of a dictionary composed of ``val`` and 

1906 ``err``. ``val`` and ``err`` are the unvalidatable value and the 

1907 exception caused trying to convert the value, respectively. These 

1908 may be used in an overridden err_template as ``${val}`` and 

1909 ``${err}`` respectively as necessary, e.g. ``_('${val} cannot be 

1910 parsed as an iso8601 date: ${err}')``. 

1911 

1912 For convenience, this type is also willing to coerce 

1913 ``datetime.datetime`` objects to a date-only ISO string 

1914 representation during serialization. It does so by stripping off 

1915 any time information, converting the ``datetime.datetime`` into a 

1916 date before serializing. 

1917 

1918 Likewise, for convenience, this type is also willing to coerce ISO 

1919 representations that contain time info into a ``datetime.date`` 

1920 object during deserialization. It does so by throwing away any 

1921 time information related to the serialized value during 

1922 deserialization. 

1923 

1924 If the :attr:`colander.null` value is passed to the serialize 

1925 method of this class, the :attr:`colander.null` value will be 

1926 returned. 

1927 

1928 The subnodes of the :class:`colander.SchemaNode` that wraps 

1929 this type are ignored. 

1930 """ 

1931 

1932 err_template = _('Invalid date') 

1933 

1934 def __init__(self, format=None): 

1935 self.format = format 

1936 

1937 def serialize(self, node, appstruct): 

1938 if not appstruct: 

1939 return null 

1940 

1941 if isinstance(appstruct, datetime.datetime): 

1942 appstruct = appstruct.date() 

1943 

1944 if not isinstance(appstruct, datetime.date): 

1945 raise Invalid( 

1946 node, 

1947 _('"${val}" is not a date object', mapping={'val': appstruct}), 

1948 ) 

1949 

1950 if self.format: 

1951 return appstruct.strftime(self.format) 

1952 return appstruct.isoformat() 

1953 

1954 def deserialize(self, node, cstruct): 

1955 if not cstruct: 

1956 return null 

1957 try: 

1958 if self.format: 

1959 result = datetime.datetime.strptime(cstruct, self.format) 

1960 else: 

1961 result = iso8601.parse_date(cstruct) 

1962 result = result.date() 

1963 except iso8601.ParseError as e: 

1964 raise Invalid( 

1965 node, _(self.err_template, mapping={'val': cstruct, 'err': e}) 

1966 ) 

1967 return result 

1968 

1969 

1970class Time(SchemaType): 

1971 """ A type representing a Python ``datetime.time`` object. 

1972 

1973 .. note:: This type is new as of Colander 0.9.3. 

1974 

1975 This type serializes python ``datetime.time`` objects to a 

1976 `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ string format. 

1977 The format includes the time only. 

1978 

1979 The constructor accepts no arguments. 

1980 

1981 You can adjust the error message reported by this class by 

1982 changing its ``err_template`` attribute in a subclass on an 

1983 instance of this class. By default, the ``err_template`` 

1984 attribute is the string ``Invalid date``. This string is used as 

1985 the interpolation subject of a dictionary composed of ``val`` and 

1986 ``err``. ``val`` and ``err`` are the unvalidatable value and the 

1987 exception caused trying to convert the value, respectively. These 

1988 may be used in an overridden err_template as ``${val}`` and 

1989 ``${err}`` respectively as necessary, e.g. ``_('${val} cannot be 

1990 parsed as an iso8601 date: ${err}')``. 

1991 

1992 For convenience, this type is also willing to coerce 

1993 ``datetime.datetime`` objects to a time-only ISO string 

1994 representation during serialization. It does so by stripping off 

1995 any date information, converting the ``datetime.datetime`` into a 

1996 time before serializing. 

1997 

1998 Likewise, for convenience, this type is also willing to coerce ISO 

1999 representations that contain time info into a ``datetime.time`` 

2000 object during deserialization. It does so by throwing away any 

2001 date information related to the serialized value during 

2002 deserialization. 

2003 

2004 If the :attr:`colander.null` value is passed to the serialize 

2005 method of this class, the :attr:`colander.null` value will be 

2006 returned. 

2007 

2008 The subnodes of the :class:`colander.SchemaNode` that wraps 

2009 this type are ignored. 

2010 """ 

2011 

2012 err_template = _('Invalid time') 

2013 

2014 def serialize(self, node, appstruct): 

2015 if isinstance(appstruct, datetime.datetime): 

2016 appstruct = appstruct.time() 

2017 

2018 if not isinstance(appstruct, datetime.time): 

2019 if not appstruct: 

2020 return null 

2021 raise Invalid( 

2022 node, 

2023 _('"${val}" is not a time object', mapping={'val': appstruct}), 

2024 ) 

2025 

2026 return appstruct.isoformat() 

2027 

2028 def deserialize(self, node, cstruct): 

2029 if not cstruct: 

2030 return null 

2031 try: 

2032 result = iso8601.parse_date(cstruct) 

2033 return result.time() 

2034 except (iso8601.ParseError, TypeError) as e: 

2035 err = e 

2036 fmts = ['%H:%M:%S.%f', '%H:%M:%S', '%H:%M'] 

2037 for fmt in fmts: 

2038 try: 

2039 return datetime.datetime.strptime(cstruct, fmt).time() 

2040 except (ValueError, TypeError): 

2041 continue 

2042 raise Invalid( 

2043 node, _(self.err_template, mapping={'val': cstruct, 'err': err}) 

2044 ) 

2045 

2046 

2047class Enum(SchemaType): 

2048 """A type representing a Python ``enum.Enum`` object. 

2049 

2050 The constructor accepts three arguments named ``enum_cls``, ``attr``, 

2051 and ``typ``. 

2052 

2053 ``enum_cls`` is a mandatory argument and it should be a subclass of 

2054 ``enum.Enum``. This argument represents the appstruct's type. 

2055 

2056 ``attr`` is an optional argument. Its default is ``name``. 

2057 It is used to pick a serialized value from an enum instance. 

2058 A serialized value must be unique. 

2059 

2060 ``typ`` is an optional argument, and it should be an instance of 

2061 ``colander.SchemaType``. This argument represents the cstruct's type. 

2062 If ``typ`` is not specified, a plain ``colander.String`` is used. 

2063 """ 

2064 

2065 def __init__(self, enum_cls, attr=None, typ=None): 

2066 self.enum_cls = enum_cls 

2067 self.attr = 'name' if attr is None else attr 

2068 self.typ = String() if typ is None else typ 

2069 if self.attr == 'name': 

2070 self.values = enum_cls.__members__.copy() 

2071 else: 

2072 self.values = {} 

2073 for e in self.enum_cls.__members__.values(): 

2074 v = getattr(e, self.attr) 

2075 if v in self.values: 

2076 raise ValueError( 

2077 '%r is not unique in %r', v, self.enum_cls 

2078 ) 

2079 self.values[v] = e 

2080 

2081 def serialize(self, node, appstruct): 

2082 if appstruct is null: 

2083 return null 

2084 

2085 if not isinstance(appstruct, self.enum_cls): 

2086 raise Invalid( 

2087 node, 

2088 _( 

2089 '"${val}" is not a valid "${cls}"', 

2090 mapping={'val': appstruct, 'cls': self.enum_cls.__name__}, 

2091 ), 

2092 ) 

2093 

2094 return self.typ.serialize(node, getattr(appstruct, self.attr)) 

2095 

2096 def deserialize(self, node, cstruct): 

2097 result = self.typ.deserialize(node, cstruct) 

2098 if result is null: 

2099 return null 

2100 

2101 if result not in self.values: 

2102 raise Invalid( 

2103 node, 

2104 _( 

2105 '"${val}" is not a valid "${cls}"', 

2106 mapping={'val': cstruct, 'cls': self.enum_cls.__name__}, 

2107 ), 

2108 ) 

2109 return self.values[result] 

2110 

2111 

2112def _add_node_children(node, children): 

2113 for n in children: 

2114 insert_before = getattr(n, 'insert_before', None) 

2115 exists = node.get(n.name, _marker) is not _marker 

2116 # use exists for microspeed; we could just call __setitem__ 

2117 # exclusively, but it does an enumeration that's unnecessary in the 

2118 # common (nonexisting) case (.add is faster) 

2119 if insert_before is None: 

2120 if exists: 

2121 node[n.name] = n 

2122 else: 

2123 node.add(n) 

2124 else: 

2125 if exists: 

2126 del node[n.name] 

2127 node.add_before(insert_before, n) 

2128 

2129 

2130class _SchemaNode(object): 

2131 """ 

2132 Fundamental building block of schemas. 

2133 

2134 The constructor accepts these positional arguments: 

2135 

2136 - ``typ``: The 'type' for this node. It should be an 

2137 instance of a class that implements the 

2138 :class:`colander.interfaces.Type` interface. If ``typ`` is not passed, 

2139 a call to the ``schema_type()`` method on this class is made to 

2140 get a default type. (When subclassing, ``schema_type()`` should 

2141 be overridden to provide a reasonable default type). 

2142 

2143 - ``*children``: a sequence of subnodes. If the subnodes of this 

2144 node are not known at construction time, they can later be added 

2145 via the ``add`` method. 

2146 

2147 The constructor accepts these keyword arguments: 

2148 

2149 - ``name``: The name of this node. 

2150 

2151 - ``typ``: The 'type' for this node can optionally be passed in as a 

2152 keyword argument. See the documentation for the positional arg above. 

2153 

2154 - ``default``: The default serialization value for this node when 

2155 not set. If ``default`` is :attr:`colander.drop`, the node 

2156 will be dropped from schema serialization. If not provided, 

2157 the node will be serialized to :attr:`colander.null`. 

2158 

2159 - ``missing``: The default deserialization value for this node. If it is 

2160 not provided, the missing value of this node will be the special marker 

2161 value :attr:`colander.required`, indicating that it is considered 

2162 'required'. When ``missing`` is :attr:`colander.required`, the 

2163 ``required`` computed attribute will be ``True``. When ``missing`` is 

2164 :attr:`colander.drop`, the node is dropped from the schema if it isn't 

2165 set during deserialization. 

2166 

2167 - ``missing_msg``: Optional error message to be used if the value is 

2168 required and missing. 

2169 

2170 - ``preparer``: Optional preparer for this node. It should be 

2171 an object that implements the 

2172 :class:`colander.interfaces.Preparer` interface. 

2173 

2174 - ``validator``: Optional validator for this node. It should be 

2175 an object that implements the 

2176 :class:`colander.interfaces.Validator` interface. 

2177 

2178 - ``after_bind``: A callback which is called after a clone of this 

2179 node has 'bound' all of its values successfully. This callback 

2180 is useful for performing arbitrary actions to the cloned node, 

2181 or direct children of the cloned node (such as removing or 

2182 adding children) at bind time. A 'binding' is the result of an 

2183 execution of the ``bind`` method of the clone's prototype node, 

2184 or one of the parents of the clone's prototype nodes. The 

2185 deepest nodes in the node tree are bound first, so the 

2186 ``after_bind`` methods of the deepest nodes are called before 

2187 the shallowest. The ``after_bind`` callback should 

2188 accept two values: ``node`` and ``kw``. ``node`` will be a 

2189 clone of the bound node object, ``kw`` will be the set of 

2190 keywords passed to the ``bind`` method. 

2191 

2192 - ``title``: The title of this node. Defaults to a titleization 

2193 of the ``name`` (underscores replaced with empty strings and the 

2194 first letter of every resulting word capitalized). The title is 

2195 used by higher-level systems (not by Colander itself). 

2196 

2197 - ``description``: The description for this node. Defaults to 

2198 ``''`` (the empty string). The description is used by 

2199 higher-level systems (not by Colander itself). 

2200 

2201 - ``widget``: The 'widget' for this node. Defaults to ``None``. 

2202 The widget attribute is not interpreted by Colander itself, it 

2203 is only meaningful to higher-level systems such as Deform. 

2204 

2205 - ``insert_before``: if supplied, it names a sibling defined by a 

2206 superclass for its parent node; the current node will be inserted 

2207 before the named node. It is not useful unless a mapping schema is 

2208 inherited from another mapping schema, and you need to control 

2209 the ordering of the resulting nodes. 

2210 

2211 Arbitrary keyword arguments remaining will be attached to the node 

2212 object unmolested. 

2213 """ 

2214 

2215 _counter = itertools.count() 

2216 preparer = None 

2217 validator = None 

2218 default = null 

2219 missing = required 

2220 missing_msg = _('Required') 

2221 name = '' 

2222 raw_title = _marker # only changes if title is explicitly set 

2223 title = _marker 

2224 description = '' 

2225 widget = None 

2226 after_bind = None 

2227 bindings = None 

2228 

2229 def __new__(cls, *args, **kw): 

2230 node = object.__new__(cls) 

2231 node._order = next(cls._counter) 

2232 node.children = [] 

2233 _add_node_children(node, cls.__all_schema_nodes__) 

2234 return node 

2235 

2236 def __init__(self, *arg, **kw): 

2237 # bw compat forces us to treat first arg as type if not a _SchemaNode 

2238 if 'typ' in kw: 

2239 self.typ = kw.pop('typ') 

2240 elif arg and not isinstance(arg[0], _SchemaNode): 

2241 self.typ, arg = arg[0], arg[1:] 

2242 else: 

2243 self.typ = self.schema_type() 

2244 _add_node_children(self, arg) 

2245 

2246 # bw compat forces us to manufacture a title if one is not supplied 

2247 title = kw.get('title', self.title) 

2248 if title is _marker: 

2249 name = kw.get('name', self.name) 

2250 kw['title'] = name.replace('_', ' ').title() 

2251 else: 

2252 kw['raw_title'] = title 

2253 

2254 self.__dict__.update(kw) 

2255 

2256 @staticmethod 

2257 def schema_type(): 

2258 raise NotImplementedError( 

2259 'Schema node construction without a `typ` argument or ' 

2260 'a schema_type() callable present on the node class ' 

2261 ) 

2262 

2263 @property 

2264 def required(self): 

2265 """ A property which returns ``True`` if the ``missing`` value 

2266 related to this node was not specified. 

2267 

2268 A return value of ``True`` implies that a ``missing`` value wasn't 

2269 specified for this node or that the ``missing`` value of this node is 

2270 :attr:`colander.required`. A return value of ``False`` implies that 

2271 a 'real' ``missing`` value was specified for this node.""" 

2272 if isinstance(self.missing, deferred): # unbound schema with deferreds 

2273 return True 

2274 return self.missing is required 

2275 

2276 def serialize(self, appstruct=null): 

2277 """ Serialize the :term:`appstruct` to a :term:`cstruct` based 

2278 on the schema represented by this node and return the 

2279 cstruct. 

2280 

2281 If ``appstruct`` is :attr:`colander.null`, return the 

2282 serialized value of this node's ``default`` attribute (by 

2283 default, the serialization of :attr:`colander.null`). 

2284 

2285 If an ``appstruct`` argument is not explicitly provided, it 

2286 defaults to :attr:`colander.null`. 

2287 """ 

2288 if appstruct is null: 

2289 appstruct = self.default 

2290 if isinstance(appstruct, deferred): # unbound schema with deferreds 

2291 appstruct = null 

2292 cstruct = self.typ.serialize(self, appstruct) 

2293 return cstruct 

2294 

2295 def flatten(self, appstruct): 

2296 """ Create and return a data structure which is a flattened 

2297 representation of the passed in struct based on the schema represented 

2298 by this node. The return data structure is a dictionary; its keys are 

2299 dotted names. Each dotted name represents a path to a location in the 

2300 schema. The values of of the flattened dictionary are subvalues of 

2301 the passed in struct.""" 

2302 flat = self.typ.flatten(self, appstruct) 

2303 return flat 

2304 

2305 def unflatten(self, fstruct): 

2306 """ Create and return a data structure with nested substructures based 

2307 on the schema represented by this node using the flattened 

2308 representation passed in. This is the inverse operation to 

2309 :meth:`colander.SchemaNode.flatten`.""" 

2310 paths = sorted(fstruct.keys()) 

2311 return self.typ.unflatten(self, paths, fstruct) 

2312 

2313 def set_value(self, appstruct, dotted_name, value): 

2314 """ Uses the schema to set a value in a nested datastructure from a 

2315 dotted name path. """ 

2316 self.typ.set_value(self, appstruct, dotted_name, value) 

2317 

2318 def get_value(self, appstruct, dotted_name): 

2319 """ Traverses the nested data structure using the schema and retrieves 

2320 the value specified by the dotted name path.""" 

2321 return self.typ.get_value(self, appstruct, dotted_name) 

2322 

2323 def deserialize(self, cstruct=null): 

2324 """ Deserialize the :term:`cstruct` into an :term:`appstruct` based 

2325 on the schema, run this :term:`appstruct` through the 

2326 preparer, if one is present, then validate the 

2327 prepared appstruct. The ``cstruct`` value is deserialized into an 

2328 ``appstruct`` unconditionally. 

2329 

2330 If ``appstruct`` returned by type deserialization and 

2331 preparation is the value :attr:`colander.null`, do something 

2332 special before attempting validation: 

2333 

2334 - If the ``missing`` attribute of this node has been set explicitly, 

2335 return its value. No validation of this value is performed; it is 

2336 simply returned. 

2337 

2338 - If the ``missing`` attribute of this node has not been set 

2339 explicitly, raise a :exc:`colander.Invalid` exception error. 

2340 

2341 If the appstruct is not ``colander.null`` and cannot be validated , a 

2342 :exc:`colander.Invalid` exception will be raised. 

2343 

2344 If a ``cstruct`` argument is not explicitly provided, it 

2345 defaults to :attr:`colander.null`. 

2346 """ 

2347 appstruct = self.typ.deserialize(self, cstruct) 

2348 

2349 if self.preparer is not None: 

2350 # if the preparer is a function, call a single preparer 

2351 if hasattr(self.preparer, '__call__'): 

2352 appstruct = self.preparer(appstruct) 

2353 # if the preparer is a list, call each separate preparer 

2354 elif is_nonstr_iter(self.preparer): 

2355 for preparer in self.preparer: 

2356 appstruct = preparer(appstruct) 

2357 

2358 if appstruct is null: 

2359 appstruct = self.missing 

2360 if appstruct is required: 

2361 raise Invalid( 

2362 self, 

2363 _( 

2364 self.missing_msg, 

2365 mapping={'title': self.title, 'name': self.name}, 

2366 ), 

2367 ) 

2368 

2369 if isinstance(appstruct, deferred): 

2370 # unbound schema with deferreds 

2371 raise Invalid(self, self.missing_msg) 

2372 # We never deserialize or validate the missing value 

2373 return appstruct 

2374 

2375 if self.validator is not None: 

2376 if isinstance(self.validator, deferred): # unbound 

2377 raise UnboundDeferredError( 

2378 "Schema node {node} has an unbound " 

2379 "deferred validator".format(node=self) 

2380 ) 

2381 self.validator(self, appstruct) 

2382 return appstruct 

2383 

2384 def add(self, node): 

2385 """ Append a subnode to this node. ``node`` must be a SchemaNode.""" 

2386 self.children.append(node) 

2387 

2388 def insert(self, index, node): 

2389 """ Insert a subnode into the position ``index``. ``node`` must be 

2390 a SchemaNode.""" 

2391 self.children.insert(index, node) 

2392 

2393 def add_before(self, name, node): 

2394 """ Insert a subnode into the position before the node named ``name`` 

2395 """ 

2396 for pos, sub in enumerate(self.children[:]): 

2397 if sub.name == name: 

2398 self.insert(pos, node) 

2399 return 

2400 raise KeyError('No such node named %s' % name) 

2401 

2402 def get(self, name, default=None): 

2403 """ Return the subnode associated with ``name`` or ``default`` if no 

2404 such node exists.""" 

2405 for node in self.children: 

2406 if node.name == name: 

2407 return node 

2408 return default 

2409 

2410 def clone(self): 

2411 """ Clone the schema node and return the clone. All subnodes 

2412 are also cloned recursively. Attributes present in node 

2413 dictionaries are preserved.""" 

2414 cloned = self.__class__(self.typ) 

2415 cloned.__dict__.update(self.__dict__) 

2416 cloned.children = [node.clone() for node in self.children] 

2417 return cloned 

2418 

2419 def bind(self, **kw): 

2420 """ Resolve any deferred values attached to this schema node 

2421 and its children (recursively), using the keywords passed as 

2422 ``kw`` as input to each deferred value. This function 

2423 *clones* the schema it is called upon and returns the cloned 

2424 value. The original schema node (the source of the clone) 

2425 is not modified.""" 

2426 cloned = self.clone() 

2427 cloned._bind(kw) 

2428 return cloned 

2429 

2430 def _bind(self, kw): 

2431 self.bindings = kw 

2432 for child in self.children: 

2433 child._bind(kw) 

2434 names = dir(self) 

2435 for k in names: 

2436 v = getattr(self, k) 

2437 if isinstance(v, deferred): 

2438 v = v(self, kw) 

2439 if isinstance(v, SchemaNode): 

2440 self[k] = v 

2441 else: 

2442 setattr(self, k, v) 

2443 if getattr(self, 'after_bind', None): 

2444 self.after_bind(self, kw) 

2445 

2446 def cstruct_children(self, cstruct): 

2447 """ Will call the node's type's ``cstruct_children`` method with this 

2448 node as a first argument, and ``cstruct`` as a second argument.""" 

2449 cstruct_children = getattr(self.typ, 'cstruct_children', None) 

2450 if cstruct_children is None: 

2451 warnings.warn( 

2452 'The node type %s has no cstruct_children method. ' 

2453 'This method is required to be implemented by schema types ' 

2454 'for compatibility with Colander 0.9.9+. In a future Colander ' 

2455 'version, the absence of this method will cause an ' 

2456 'exception. Returning [] for compatibility although it ' 

2457 'may not be the right value.' % self.typ.__class__, 

2458 DeprecationWarning, 

2459 stacklevel=2, 

2460 ) 

2461 return [] 

2462 return cstruct_children(self, cstruct) 

2463 

2464 def __delitem__(self, name): 

2465 """ Remove a subnode by name """ 

2466 for idx, node in enumerate(self.children[:]): 

2467 if node.name == name: 

2468 return self.children.pop(idx) 

2469 raise KeyError(name) 

2470 

2471 def __getitem__(self, name): 

2472 """ Get a subnode by name. """ 

2473 val = self.get(name, _marker) 

2474 if val is _marker: 

2475 raise KeyError(name) 

2476 return val 

2477 

2478 def __setitem__(self, name, newnode): 

2479 """ Replace a subnode by name. ``newnode`` must be a SchemaNode. If 

2480 a subnode named ``name`` doesn't already exist, calling this method 

2481 is the same as setting the node's name to ``name`` and calling the 

2482 ``add`` method with the node (it will be appended to the children 

2483 list).""" 

2484 newnode.name = name 

2485 for idx, node in enumerate(self.children[:]): 

2486 if node.name == name: 

2487 self.children[idx] = newnode 

2488 return node 

2489 self.add(newnode) 

2490 

2491 def __iter__(self): 

2492 """ Iterate over the children nodes of this schema node """ 

2493 return iter(self.children) 

2494 

2495 def __contains__(self, name): 

2496 """ Return True if subnode named ``name`` exists in this node """ 

2497 return self.get(name, _marker) is not _marker 

2498 

2499 def __repr__(self): 

2500 return '<%s.%s object at %d (named %s)>' % ( 

2501 self.__module__, 

2502 self.__class__.__name__, 

2503 id(self), 

2504 self.name, 

2505 ) 

2506 

2507 def raise_invalid(self, msg, node=None): 

2508 """ Raise a :exc:`colander.Invalid` exception with the message 

2509 ``msg``. ``node``, if supplied, should be an instance of a 

2510 :class:`colander.SchemaNode`. If it is not supplied, ``node`` will 

2511 be this node. Example usage:: 

2512 

2513 class CustomSchemaNode(SchemaNode): 

2514 def validator(self, node, cstruct): 

2515 if cstruct != 'the_right_thing': 

2516 self.raise_invalid('Not the right thing') 

2517 

2518 """ 

2519 if node is None: 

2520 node = self 

2521 raise Invalid(node, msg) 

2522 

2523 

2524class _SchemaMeta(type): 

2525 def __init__(cls, name, bases, clsattrs): 

2526 nodes = [] 

2527 

2528 for name, value in clsattrs.items(): 

2529 if isinstance(value, _SchemaNode): 

2530 delattr(cls, name) 

2531 if not value.name: 

2532 value.name = name 

2533 if value.raw_title is _marker: 

2534 value.title = name.replace('_', ' ').title() 

2535 nodes.append((value._order, value)) 

2536 

2537 nodes.sort(key=lambda n: n[0]) 

2538 cls.__class_schema_nodes__ = [n[1] for n in nodes] 

2539 

2540 # Combine all attrs from this class and its _SchemaNode superclasses. 

2541 cls.__all_schema_nodes__ = [] 

2542 for c in reversed(cls.__mro__): 

2543 csn = getattr(c, '__class_schema_nodes__', []) 

2544 cls.__all_schema_nodes__.extend(csn) 

2545 

2546 

2547# metaclass spelling compatibility across Python 2 and Python 3 

2548SchemaNode = _SchemaMeta( 

2549 'SchemaNode', (_SchemaNode,), {'__doc__': _SchemaNode.__doc__} 

2550) 

2551 

2552 

2553class Schema(SchemaNode): 

2554 schema_type = Mapping 

2555 

2556 

2557MappingSchema = Schema 

2558 

2559 

2560class TupleSchema(SchemaNode): 

2561 schema_type = Tuple 

2562 

2563 

2564class SequenceSchema(SchemaNode): 

2565 schema_type = Sequence 

2566 

2567 def __init__(self, *args, **kw): 

2568 SchemaNode.__init__(self, *args, **kw) 

2569 if len(self.children) != 1: 

2570 raise Invalid( 

2571 self, 'Sequence schemas must have exactly one child node' 

2572 ) 

2573 

2574 def clone(self): 

2575 """ Clone the schema node and return the clone. All subnodes 

2576 are also cloned recursively. Attributes present in node 

2577 dictionaries are preserved.""" 

2578 

2579 # Cloning a ``SequenceSchema`` doesn't work with ``_SchemaNode.clone``. 

2580 

2581 children = [node.clone() for node in self.children] 

2582 cloned = self.__class__(self.typ, *children) 

2583 

2584 attributes = self.__dict__.copy() 

2585 attributes.pop('children', None) 

2586 cloned.__dict__.update(attributes) 

2587 return cloned 

2588 

2589 

2590class deferred(object): 

2591 """ A decorator which can be used to define deferred schema values 

2592 (missing values, widgets, validators, etc.)""" 

2593 

2594 def __init__(self, wrapped): 

2595 try: 

2596 functools.update_wrapper(self, wrapped) 

2597 except AttributeError: 

2598 # non-function (raises in Python 2) 

2599 self.__doc__ = getattr(wrapped, '__doc__', None) 

2600 self.wrapped = wrapped 

2601 

2602 def __call__(self, node, kw): 

2603 return self.wrapped(node, kw) 

2604 

2605 

2606def _unflatten_mapping( 

2607 node, paths, fstruct, get_child=None, rewrite_subpath=None 

2608): 

2609 if get_child is None: 

2610 get_child = node.__getitem__ 

2611 if rewrite_subpath is None: 

2612 

2613 def rewrite_subpath(subpath): 

2614 return subpath 

2615 

2616 node_name = node.name 

2617 if node_name: 

2618 prefix = node_name + '.' 

2619 else: 

2620 prefix = '' 

2621 prefix_len = len(prefix) 

2622 appstruct = {} 

2623 subfstruct = {} 

2624 subpaths = [] 

2625 curname = None 

2626 for path in paths: 

2627 if path == node_name: 

2628 # flattened structs contain non-leaf nodes which are ignored 

2629 # during unflattening. 

2630 continue 

2631 assert path.startswith(prefix), "Bad node: %s" % path 

2632 subpath = path[prefix_len:] 

2633 if '.' in subpath: 

2634 name = subpath[: subpath.index('.')] 

2635 else: 

2636 name = subpath 

2637 if curname is None: 

2638 curname = name 

2639 elif name != curname: 

2640 subnode = get_child(curname) 

2641 appstruct[curname] = subnode.typ.unflatten( 

2642 subnode, subpaths, subfstruct 

2643 ) 

2644 subfstruct = {} 

2645 subpaths = [] 

2646 curname = name 

2647 subpath = rewrite_subpath(subpath) 

2648 subfstruct[subpath] = fstruct[path] 

2649 subpaths.append(subpath) 

2650 if curname is not None: 

2651 subnode = get_child(curname) 

2652 appstruct[curname] = subnode.typ.unflatten( 

2653 subnode, subpaths, subfstruct 

2654 ) 

2655 return appstruct 

2656 

2657 

2658class instantiate(object): 

2659 """ 

2660 A decorator which can be used to instantiate :class:`SchemaNode` 

2661 elements inline within a class definition. 

2662 

2663 All parameters passed to the decorator and passed along to the 

2664 :class:`SchemaNode` during instantiation. 

2665 """ 

2666 

2667 def __init__(self, *args, **kw): 

2668 self.args, self.kw = args, kw 

2669 

2670 def __call__(self, class_): 

2671 return class_(*self.args, **self.kw)