Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/serde/fields.py: 44%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

452 statements  

1""" 

2This module contains field classes for use with `Models <serde.Model>`. 

3""" 

4 

5import collections 

6import datetime 

7import decimal 

8import re 

9import uuid 

10from collections.abc import Mapping as MappingType 

11 

12from serde.exceptions import ContextError, ValidationError, add_context 

13from serde.utils import is_subclass, try_lookup, zip_equal 

14 

15 

16def _resolve(thing, none_allowed=True): 

17 """ 

18 Resolve an arbitrary object to a `Field` instance. 

19 

20 Args: 

21 thing: anything to resolve to a `Field` instance. 

22 none_allowed (bool): if set then a thing of `None` will be resolved to a 

23 generic `Field`. 

24 

25 Returns: 

26 Field: a field instance. 

27 """ 

28 from serde.model import Model 

29 

30 # If the thing is None then return a generic Field instance. 

31 if none_allowed and thing is None: 

32 return Field() 

33 # If the thing is a Field instance then thats great. 

34 elif isinstance(thing, Field): 

35 return thing 

36 # If the thing is a subclass of Field then attempt to create an instance. 

37 # This could fail the Field expects positional arguments. 

38 if is_subclass(thing, Field): 

39 return thing() 

40 # If the thing is a subclass of Model then create a Nested instance. 

41 if is_subclass(thing, Model): 

42 return Nested(thing) 

43 

44 # If the thing is a built-in type that we support then create an Instance 

45 # with that type. 

46 try: 

47 return _FIELD_CLASS_MAP[thing]() 

48 except (KeyError, TypeError): 

49 pass 

50 

51 raise TypeError(f'failed to resolve {thing!r} into a field') 

52 

53 

54class _Base(object): 

55 """ 

56 A base field or tag on a `~serde.Model`. 

57 

58 Fields and tags handle serializing and deserializing of input values for 

59 models. This class serves as a base class for both fields and tags. 

60 

61 Args: 

62 serializers (list): a list of serializer functions taking the value to 

63 serialize as an argument. The functions need to raise an `Exception` 

64 if they fail. These serializer functions will be applied before the 

65 primary serializer on this base field. 

66 deserializers (list): a list of deserializer functions taking the value 

67 to deserialize as an argument. The functions need to raise an 

68 `Exception` if they fail. These deserializer functions will be 

69 applied after the primary deserializer on this base field. 

70 """ 

71 

72 # This is so we can get the order the bases were instantiated in. 

73 _counter = 1 

74 

75 def __init__(self, serializers=None, deserializers=None): 

76 """ 

77 Create a new base field. 

78 """ 

79 self.id = _Base._counter 

80 _Base._counter += 1 

81 self.serializers = serializers or [] 

82 self.deserializers = deserializers or [] 

83 

84 def __eq__(self, other): 

85 """ 

86 Whether two base fields are the same. 

87 """ 

88 return isinstance(other, self.__class__) and self._attrs() == other._attrs() 

89 

90 @property 

91 def __model__(self): 

92 """ 

93 The model class that the base field is bound to. 

94 """ 

95 return self._model_cls 

96 

97 def _attrs(self): 

98 """ 

99 Returns a dictionary of all public attributes on this base field. 

100 """ 

101 return { 

102 name: value 

103 for name, value in vars(self).items() 

104 if name not in ('id', '_model_cls') 

105 } 

106 

107 def _bind(self, model_cls): 

108 """ 

109 Bind the base field to a model class. 

110 """ 

111 if hasattr(self, '_model_cls'): 

112 raise ContextError( 

113 f'attempted to use {self.__class__.__name__!r} instance more than once' 

114 ) 

115 self._model_cls = model_cls 

116 

117 def _serialize_with(self, model, d): 

118 """ 

119 Serialize value(s) from a model instance to a dictionary. 

120 

121 This method needs to be overridden and should use ``self._serialize()`` 

122 on individual values. 

123 

124 Args: 

125 model (Model): the model instance that we are serializing from. 

126 d (dict): the dictionary to serialize to. 

127 

128 Returns: 

129 dict: the updated dictionary to continue serializing to. 

130 """ 

131 raise NotImplementedError('this method should be overridden') 

132 

133 def _deserialize_with(self, model, d): 

134 """ 

135 Deserialize value(s) from a dictionary to a model instance. 

136 

137 This method needs to be overridden and should use 

138 ``self._deserialize()`` on individual values. 

139 

140 Args: 

141 model (Model): the model instance that we are deserializing to. 

142 d (dict): the dictionary to deserialize from. 

143 

144 Returns: 

145 (model, dict): the updated Model instance to continue deserializing 

146 to and the updated dictionary to continue deserializing from. 

147 """ 

148 raise NotImplementedError('this method should be overridden') 

149 

150 def _serialize(self, value): 

151 for serializer in self.serializers: 

152 value = serializer(value) 

153 return self.serialize(value) 

154 

155 def _deserialize(self, value): 

156 value = self.deserialize(value) 

157 for deserializer in self.deserializers: 

158 value = deserializer(value) 

159 return value 

160 

161 def serialize(self, value): 

162 """ 

163 Serialize a value according to this base field's specification. 

164 """ 

165 return value 

166 

167 def deserialize(self, value): 

168 """ 

169 Deserialize a value according to this base field's specification. 

170 """ 

171 return value 

172 

173 

174class Field(_Base): 

175 """ 

176 A field on a `~serde.Model`. 

177 

178 Fields handle serializing, deserializing, normalization, and validation of 

179 input values for `~serde.Model` objects. 

180 

181 Args: 

182 rename (str): override the name for the field when serializing and 

183 expect this name when deserializing. 

184 default: a value to use if there is no input field value or the input 

185 value is `None`. This can also be a callable that generates the 

186 default. The callable must take no positional arguments. This 

187 default only applies to instantiated values. Field values are still 

188 required on deserialization. 

189 serializers (list): a list of serializer functions taking the value to 

190 serialize as an argument. The functions need to raise an `Exception` 

191 if they fail. These serializer functions will be applied before the 

192 primary serializer on this Field. 

193 deserializers (list): a list of deserializer functions taking the value 

194 to deserialize as an argument. The functions need to raise an 

195 `Exception` if they fail. These deserializer functions will be 

196 applied after the primary deserializer on this Field. 

197 normalizers (list): a list of normalizer functions taking the value to 

198 normalize as an argument. The functions need to raise an `Exception` 

199 if they fail. These normalizer functions will be applied after the 

200 primary normalizer on this Field. 

201 validators (list): a list of validator functions taking the value to 

202 validate as an argument. The functions need to raise an `Exception` 

203 if they fail. 

204 """ 

205 

206 def __init__( 

207 self, 

208 rename=None, 

209 default=None, 

210 serializers=None, 

211 deserializers=None, 

212 normalizers=None, 

213 validators=None, 

214 ): 

215 """ 

216 Create a new `Field`. 

217 """ 

218 super(Field, self).__init__( 

219 serializers=serializers, deserializers=deserializers 

220 ) 

221 self.rename = rename 

222 self.default = default 

223 self.normalizers = normalizers or [] 

224 self.validators = validators or [] 

225 

226 def _attrs(self): 

227 """ 

228 Returns a dictionary of all public attributes on this field. 

229 """ 

230 return { 

231 name: value 

232 for name, value in vars(self).items() 

233 if name not in ('id', '_model_cls', '_attr_name', '_serde_name') 

234 } 

235 

236 def _default(self): 

237 """ 

238 Call the default function or return the default value. 

239 """ 

240 return self.default() if callable(self.default) else self.default 

241 

242 def _bind(self, model_cls, name): 

243 """ 

244 Bind the field to a model class with an attribute name. 

245 """ 

246 super(Field, self)._bind(model_cls) 

247 self._attr_name = name 

248 self._serde_name = self.rename if self.rename else name 

249 

250 def _instantiate_with(self, model, kwargs): 

251 """ 

252 Instantiate the corresponding model attribute from the keyword args. 

253 

254 This method should .pop() from kwargs. 

255 """ 

256 value = self._instantiate(kwargs.pop(self._attr_name, None)) 

257 if value is None: 

258 raise TypeError(f'__init__() missing required argument {self._attr_name!r}') 

259 setattr(model, self._attr_name, value) 

260 

261 def _serialize_with(self, model, d): 

262 """ 

263 Serialize the corresponding model attribute to a dictionary. 

264 """ 

265 d[self._serde_name] = self._serialize(getattr(model, self._attr_name)) 

266 return d 

267 

268 def _deserialize_with(self, model, d): 

269 """ 

270 Deserialize the corresponding model attribute from a dictionary. 

271 """ 

272 try: 

273 value = d[self._serde_name] 

274 except KeyError: 

275 raise ValidationError(f'missing data, expected field {self._serde_name!r}') 

276 setattr(model, self._attr_name, self._deserialize(value)) 

277 return model, d 

278 

279 def _normalize_with(self, model): 

280 """ 

281 Normalize the model attribute according to this field's specification. 

282 """ 

283 value = getattr(model, self._attr_name) 

284 setattr(model, self._attr_name, self._normalize(value)) 

285 

286 def _validate_with(self, model): 

287 """ 

288 Validate the model attribute according to this field's specification. 

289 """ 

290 self._validate(getattr(model, self._attr_name)) 

291 

292 def _instantiate(self, value): 

293 return self._default() if value is None else value 

294 

295 def _normalize(self, value): 

296 value = self.normalize(value) 

297 for normalizer in self.normalizers: 

298 value = normalizer(value) 

299 return value 

300 

301 def _validate(self, value): 

302 self.validate(value) 

303 for validator in self.validators: 

304 validator(value) 

305 

306 def normalize(self, value): 

307 """ 

308 Normalize a value according to this field's specification. 

309 

310 By default this method does not do anything. 

311 """ 

312 return value 

313 

314 def validate(self, value): 

315 """ 

316 Validate a value according to this field's specification. 

317 

318 By default this method does not do anything. 

319 """ 

320 pass 

321 

322 

323class Optional(Field): 

324 """ 

325 An optional field. 

326 

327 An `Optional` is a field that is allowed to be `None`. Serialization, 

328 normalization, deserialization, and validation using the wrapped field will 

329 only be called if the value is not `None`. 

330 

331 Args: 

332 inner: the `Field` class/instance that this `Optional` wraps. 

333 default: a value to use if there is no input field value or the input 

334 value is `None`. This can also be a callable that generates the 

335 default. The callable must take no positional arguments. 

336 **kwargs: keyword arguments for the `Field` constructor. 

337 """ 

338 

339 def __init__(self, inner=None, **kwargs): 

340 """ 

341 Create a new `Optional`. 

342 """ 

343 super(Optional, self).__init__(**kwargs) 

344 self.inner = _resolve(inner) 

345 

346 def _instantiate_with(self, model, kwargs): 

347 """ 

348 Instantiate the corresponding model attribute from the keyword args. 

349 

350 This method should .pop() from kwargs. 

351 """ 

352 name = self._attr_name 

353 setattr(model, name, self._instantiate(kwargs.pop(name, None))) 

354 

355 def _serialize_with(self, model, d): 

356 """ 

357 Serialize the corresponding model attribute to a dictionary. 

358 

359 The value will only be added to the dictionary if it is not `None`. 

360 """ 

361 value = self._serialize(getattr(model, self._attr_name)) 

362 if value is not None: 

363 d[self._serde_name] = value 

364 return d 

365 

366 def _deserialize_with(self, model, d): 

367 """ 

368 Deserialize the corresponding model attribute from a dictionary. 

369 

370 If the field is not present in the dictionary then the model instance is 

371 left unchanged. 

372 """ 

373 try: 

374 value = d[self._serde_name] 

375 except KeyError: 

376 return model, d 

377 setattr(model, self._attr_name, self._deserialize(value)) 

378 return model, d 

379 

380 def _normalize_with(self, model): 

381 """ 

382 Normalize the model attribute. 

383 """ 

384 value = self._normalize(getattr(model, self._attr_name, None)) 

385 setattr(model, self._attr_name, value) 

386 

387 def _instantiate(self, value): 

388 return value 

389 

390 def _serialize(self, value): 

391 if value is not None: 

392 value = self.serialize(value) 

393 for serializer in self.serializers: 

394 value = serializer(value) 

395 return value 

396 

397 def _deserialize(self, value): 

398 if value is not None: 

399 value = self.deserialize(value) 

400 for deserializer in self.deserializers: 

401 value = deserializer(value) 

402 return value 

403 

404 def _normalize(self, value): 

405 if value is not None: 

406 value = self.normalize(value) 

407 for normalizer in self.normalizers: 

408 value = normalizer(value) 

409 else: 

410 value = self._default() 

411 return value 

412 

413 def _validate(self, value): 

414 if value is not None: 

415 self.validate(value) 

416 for validator in self.validators: 

417 validator(value) 

418 

419 def serialize(self, value): 

420 """ 

421 Serialize the given value using the inner `Field`. 

422 """ 

423 return self.inner._serialize(value) 

424 

425 def deserialize(self, value): 

426 """ 

427 Deserialize the given value using the inner `Field`. 

428 """ 

429 return self.inner._deserialize(value) 

430 

431 def normalize(self, value): 

432 """ 

433 Normalize the given value using the inner `Field`. 

434 """ 

435 return self.inner._normalize(value) 

436 

437 def validate(self, value): 

438 """ 

439 Validate the given value using the inner `Field`. 

440 """ 

441 self.inner._validate(value) 

442 

443 

444class Instance(Field): 

445 """ 

446 A field that is an instance of a type. 

447 

448 An `Instance` field simply validates that the data is the specified type. 

449 

450 Args: 

451 type: the type that this `Instance` wraps. 

452 **kwargs: keyword arguments for the `Field` constructor. 

453 """ 

454 

455 def __init__(self, ty, **kwargs): 

456 """ 

457 Create a new `Instance`. 

458 """ 

459 super(Instance, self).__init__(**kwargs) 

460 self.ty = ty 

461 

462 def validate(self, value): 

463 """ 

464 Validate the given value is an instance of the specified type. 

465 """ 

466 if not isinstance(value, self.ty): 

467 raise ValidationError( 

468 f'invalid type, expected {self.ty.__name__!r}', value=value 

469 ) 

470 

471 

472class Nested(Instance): 

473 """ 

474 A field for `~serde.Model` fields. 

475 

476 A `Nested` is a wrapper field for models to support sub-models. The 

477 serialize and deserialize methods call the `~serde.Model.to_dict()` and 

478 `~serde.Model.from_dict()` methods on the model class. This allows complex 

479 nested models. 

480 

481 Args: 

482 model_cls (serde.Model): the nested model class. 

483 **kwargs: keyword arguments for the `Field` constructor. 

484 """ 

485 

486 def serialize(self, model): 

487 """ 

488 Serialize the given `~serde.Model` instance as a dictionary. 

489 """ 

490 return model.to_dict() 

491 

492 def deserialize(self, d): 

493 """ 

494 Deserialize the given dictionary to a `~serde.Model` instance. 

495 """ 

496 if not isinstance(d, MappingType): 

497 raise ValidationError("invalid type, expected 'mapping'", value=d) 

498 return self.ty.from_dict(d) 

499 

500 

501class Flatten(Nested): 

502 """ 

503 A field that flattens the serialized version of the wrapped `~serde.Model` 

504 into the parent dictionary. 

505 

506 This effectively removes one level of structure between the serialized 

507 representation and the Python model representation. 

508 

509 Warning: 

510 this field cannot be contained by another like an `Optional`, or a 

511 `List` or `Dict`. 

512 

513 Args: 

514 model_cls (serde.Model): the nested model class. 

515 **kwargs: keyword arguments for the `Field` constructor. 

516 """ 

517 

518 def _serialize_with(self, model, d): 

519 """ 

520 Serialize the corresponding nested model attribute to a dictionary. 

521 """ 

522 d.update(self._serialize(getattr(model, self._attr_name))) 

523 return d 

524 

525 def _deserialize_with(self, model, d): 

526 """ 

527 Deserialize the corresponding model attribute from a dictionary. 

528 """ 

529 setattr(model, self._attr_name, self._deserialize(d)) 

530 return model, d 

531 

532 

533class _Container(Instance): 

534 """ 

535 A base class for `Dict`, `List`, `Tuple`, and other container fields. 

536 """ 

537 

538 def __init__(self, ty, **kwargs): 

539 """ 

540 Create a new `_Container`. 

541 """ 

542 super(_Container, self).__init__(ty, **kwargs) 

543 self.kwargs = {} 

544 

545 def _iter(self, value): 

546 """ 

547 Iterate over the container. 

548 """ 

549 raise NotImplementedError() 

550 

551 def _apply(self, stage, element): 

552 """ 

553 Apply a stage to a particular element in the container. 

554 """ 

555 raise NotImplementedError() 

556 

557 def serialize(self, value): 

558 """ 

559 Serialize the given container. 

560 

561 Each element in the container will be serialized with the specified 

562 field instances. 

563 """ 

564 value = self.ty( 

565 (self._apply('_serialize', element) for element in self._iter(value)), 

566 **self.kwargs, 

567 ) 

568 return super(_Container, self).serialize(value) 

569 

570 def deserialize(self, value): 

571 """ 

572 Deserialize the given container. 

573 

574 Each element in the container will be deserialized with the specified 

575 field instances. 

576 """ 

577 value = super(_Container, self).deserialize(value) 

578 return self.ty( 

579 (self._apply('_deserialize', element) for element in self._iter(value)), 

580 **self.kwargs, 

581 ) 

582 

583 def normalize(self, value): 

584 """ 

585 Deserialize the given container. 

586 

587 Each element in the container will be normalized with the specified 

588 field instances. 

589 """ 

590 value = super(_Container, self).normalize(value) 

591 return self.ty( 

592 (self._apply('_normalize', element) for element in self._iter(value)), 

593 **self.kwargs, 

594 ) 

595 

596 def validate(self, value): 

597 """ 

598 Validate the given container. 

599 

600 Each element in the container will be validated with the specified field 

601 instances. 

602 """ 

603 super(_Container, self).validate(value) 

604 for element in self._iter(value): 

605 self._apply('_validate', element) 

606 

607 

608class _Mapping(_Container): 

609 """ 

610 A mapping field to be used as the base class for `Dict` and `OrderedDict`. 

611 """ 

612 

613 def __init__(self, ty, key=None, value=None, **kwargs): 

614 super(_Mapping, self).__init__(ty, **kwargs) 

615 self.key = _resolve(key) 

616 self.value = _resolve(value) 

617 

618 def _iter(self, value): 

619 """ 

620 Iterate over the mapping's items. 

621 """ 

622 try: 

623 for element in value.items(): 

624 yield element 

625 except (AttributeError, TypeError): 

626 raise ValidationError( 

627 f'invalid type, expected {self.ty.__name__!r}', value=value 

628 ) 

629 

630 def _apply(self, stage, element): 

631 """ 

632 Apply the key stage to each key, and the value stage to each value. 

633 """ 

634 key, value = element 

635 with add_context(key): 

636 return (getattr(self.key, stage)(key), getattr(self.value, stage)(value)) 

637 

638 

639class Dict(_Mapping): 

640 """ 

641 This field represents the built-in `dict` type. 

642 

643 Args: 

644 key: the `Field` class or instance for keys in this `Dict`. 

645 value: the `Field` class or instance for values in this `Dict`. 

646 **kwargs: keyword arguments for the `Field` constructor. 

647 """ 

648 

649 def __init__(self, key=None, value=None, **kwargs): 

650 """ 

651 Create a new `Dict`. 

652 """ 

653 super(Dict, self).__init__(dict, key=key, value=value, **kwargs) 

654 

655 

656class OrderedDict(_Mapping): 

657 """ 

658 An `~collections.OrderedDict` field. 

659 

660 Args: 

661 key: the `Field` class or instance for keys in this `OrderedDict`. 

662 value: the `Field` class or instance for values in this `OrderedDict`. 

663 **kwargs: keyword arguments for the `Field` constructor. 

664 """ 

665 

666 def __init__(self, key=None, value=None, **kwargs): 

667 """ 

668 Create a new `OrderedDict`. 

669 """ 

670 super(OrderedDict, self).__init__( 

671 collections.OrderedDict, key=key, value=value, **kwargs 

672 ) 

673 

674 

675class _Sequence(_Container): 

676 """ 

677 A sequence field to be used as the base class for fields such as `List` and `Set` 

678 """ 

679 

680 def __init__(self, ty, element=None, **kwargs): 

681 super(_Sequence, self).__init__(ty, **kwargs) 

682 self.element = _resolve(element) 

683 

684 def _iter(self, value): 

685 """ 

686 Iterate over the sequence. 

687 """ 

688 try: 

689 for element in enumerate(value): 

690 yield element 

691 except TypeError: 

692 raise ValidationError( 

693 f'invalid type, expected {self.ty.__name__!r}', value=value 

694 ) 

695 

696 def _apply(self, stage, element): 

697 """ 

698 Apply a stage to a particular element in the container. 

699 """ 

700 index, value = element 

701 with add_context(index): 

702 return getattr(self.element, stage)(value) 

703 

704 

705class Deque(_Sequence): 

706 """ 

707 A `~collections.deque` field. 

708 

709 Args: 

710 element: the `Field` class or instance for elements in the `Deque`. 

711 maxlen (int): the maximum length of this `Deque`. 

712 **kwargs: keyword arguments for the `Field` constructor. 

713 """ 

714 

715 def __init__(self, element=None, maxlen=None, **kwargs): 

716 """ 

717 Create a new `Deque`. 

718 """ 

719 super(Deque, self).__init__(collections.deque, element=element, **kwargs) 

720 self.kwargs = {'maxlen': maxlen} 

721 

722 @property 

723 def maxlen(self): 

724 """ 

725 The maximum length of this `Deque`. 

726 """ 

727 return self.kwargs['maxlen'] 

728 

729 def validate(self, value): 

730 """ 

731 Validate the given deque. 

732 """ 

733 super(Deque, self).validate(value) 

734 if value.maxlen != self.maxlen: 

735 raise ValidationError( 

736 f'invalid max length, expected {self.maxlen}', value=value 

737 ) 

738 

739 

740class FrozenSet(_Sequence): 

741 """ 

742 This field represents the built-in `frozenset` type. 

743 

744 Args: 

745 element: the `Field` class or instance for elements in the `Set`. 

746 **kwargs: keyword arguments for the `Field` constructor. 

747 """ 

748 

749 def __init__(self, element=None, **kwargs): 

750 """ 

751 Create a new `FrozenSet`. 

752 """ 

753 super(FrozenSet, self).__init__(frozenset, element=element, **kwargs) 

754 

755 

756class List(_Sequence): 

757 """ 

758 This field represents the built-in `list` type. 

759 

760 Args: 

761 element: the `Field` class or instance for elements in the `List`. 

762 **kwargs: keyword arguments for the `Field` constructor. 

763 """ 

764 

765 def __init__(self, element=None, **kwargs): 

766 """ 

767 Create a new `List`. 

768 """ 

769 super(List, self).__init__(list, element=element, **kwargs) 

770 

771 

772class Set(_Sequence): 

773 """ 

774 This field represents the built-in `set` type. 

775 

776 Args: 

777 element: the `Field` class or instance for elements in the `Set`. 

778 **kwargs: keyword arguments for the `Field` constructor. 

779 """ 

780 

781 def __init__(self, element=None, **kwargs): 

782 """ 

783 Create a new `Set`. 

784 """ 

785 super(Set, self).__init__(set, element=element, **kwargs) 

786 

787 

788class Tuple(_Sequence): 

789 """ 

790 This field represents the built-in `tuple` type. 

791 

792 Args: 

793 *elements: the `Field` classes or instances for elements in this tuple. 

794 **kwargs: keyword arguments for the `Field` constructor. 

795 """ 

796 

797 def __init__(self, *elements, **kwargs): 

798 """ 

799 Create a new `Tuple`. 

800 """ 

801 super(_Sequence, self).__init__(tuple, **kwargs) 

802 self.elements = tuple(_resolve(e, none_allowed=False) for e in elements) 

803 

804 def _iter(self, value): 

805 """ 

806 Iterate over the fields and each element in the tuple. 

807 """ 

808 try: 

809 for element in zip_equal(self.elements, super(Tuple, self)._iter(value)): 

810 yield element 

811 except ValueError: 

812 raise ValidationError( 

813 f'invalid length, expected {len(self.elements)} elements', 

814 value=value, 

815 ) 

816 

817 def _apply(self, stage, element): 

818 """ 

819 Apply the element field stage to the corresponding element value. 

820 """ 

821 field, (index, value) = element 

822 with add_context(index): 

823 return getattr(field, stage)(value) 

824 

825 

826def create_primitive(name, ty): 

827 """ 

828 Create a primitive `Field` class. 

829 """ 

830 doc = f"""This field represents the built-in `{ty}` type. 

831 

832Args: 

833 **kwargs: keyword arguments for the `Field` constructor. 

834""" 

835 

836 def __init__(self, **kwargs): # noqa: N807 

837 Instance.__init__(self, ty, **kwargs) 

838 

839 __init__.__doc__ = f'Create a new `{name}`.' 

840 

841 return type(name, (Instance,), {'__doc__': doc, '__init__': __init__}) 

842 

843 

844Bool = create_primitive('Bool', bool) 

845Complex = create_primitive('Complex', complex) 

846Float = create_primitive('Float', float) 

847Int = create_primitive('Int', int) 

848Str = create_primitive('Str', str) 

849Bytes = create_primitive('Bytes', bytes) 

850 

851del create_primitive 

852 

853 

854# A helper function... 

855def round_decimal( 

856 decimal_obj: decimal.Decimal, num_of_places: int = 6 

857) -> decimal.Decimal: 

858 return decimal_obj.quantize(decimal.Decimal(10) ** -num_of_places).normalize() 

859 

860 

861class Decimal(Instance): 

862 """ 

863 A `~decimal.Decimal` field. 

864 

865 This field serializes `~decimal.Decimal` objects as strings and 

866 deserializes string representations of Decimals as `~decimal.Decimal` 

867 objects. 

868 

869 The resolution of the decimal can be specified. When not specified, the number 

870 is not rounded. When it is specified, the decimal is rounded to this number of 

871 decimal places upon serialization and deserialization. 

872 

873 Note: When float type numbers are not rounded before serialization, 

874 they will be serialized in exact form, which as they are floats, 

875 is almost never the exact intended value, 

876 e.g. 0.2 = 0.20000000000000000000023 

877 

878 Args: 

879 resolution (Union[int, bool]): The number of decimal places to round to. 

880 When None, rounding is disabled. 

881 **kwargs: keyword arguments for the `Field` constructor. 

882 """ 

883 

884 ty = decimal.Decimal 

885 

886 def __init__(self, resolution=None, **kwargs): 

887 super(Decimal, self).__init__(self.__class__.ty, **kwargs) 

888 self.resolution = resolution 

889 

890 def serialize(self, value: decimal.Decimal) -> str: 

891 if self.resolution is not None: 

892 value = round_decimal(value, num_of_places=self.resolution) 

893 return '{0:f}'.format(value) 

894 

895 def deserialize(self, value) -> decimal.Decimal: 

896 try: 

897 if self.resolution is not None: 

898 return round_decimal( 

899 decimal.Decimal(value), num_of_places=self.resolution 

900 ) 

901 

902 return decimal.Decimal(value) 

903 except decimal.DecimalException: 

904 raise ValidationError('invalid decimal', value=value) 

905 

906 

907class Literal(Field): 

908 """ 

909 A literal field. 

910 

911 A `Literal` is a field that always has to be the specified value. 

912 

913 Args: 

914 value: the value that this `Literal` wraps. 

915 **kwargs: keyword arguments for the `Field` constructor. 

916 """ 

917 

918 def __init__(self, value, **kwargs): 

919 """ 

920 Create a new `Literal`. 

921 """ 

922 super(Literal, self).__init__(**kwargs) 

923 self.value = value 

924 

925 def validate(self, value): 

926 """ 

927 Validate that the given value is equal to the value. 

928 """ 

929 if value != self.value: 

930 raise ValidationError( 

931 f'invalid literal, expected {self.value!r}', value=value 

932 ) 

933 

934 

935class Choice(Field): 

936 """ 

937 One of a given selection of values. 

938 

939 A `Choice` field checks if the input data is one of the allowed values. 

940 These values do not need to be the same type. 

941 

942 Args: 

943 choices: a list or range or tuple of allowed values. 

944 **kwargs: keyword arguments for the `Field` constructor. 

945 """ 

946 

947 def __init__(self, choices, **kwargs): 

948 """ 

949 Create a new `Choice`. 

950 """ 

951 super(Choice, self).__init__(**kwargs) 

952 self.choices = choices 

953 

954 def validate(self, value): 

955 """ 

956 Validate that the given value is one of the choices. 

957 """ 

958 super(Choice, self).validate(value) 

959 if value not in self.choices: 

960 raise ValidationError('invalid choice', value=value) 

961 

962 

963class DateTime(Instance): 

964 """ 

965 A `~datetime.datetime` field. 

966 

967 This field serializes `~datetime.datetime` objects as strings and 

968 deserializes string representations of datetimes as `~datetime.datetime` 

969 objects. 

970 

971 The date format can be specified. It will default to ISO 8601. 

972 

973 Args: 

974 format (str): the datetime format to use. "iso8601" may be used for 

975 ISO 8601 datetimes. 

976 **kwargs: keyword arguments for the `Field` constructor. 

977 """ 

978 

979 ty = datetime.datetime 

980 

981 def __init__(self, format='iso8601', **kwargs): 

982 """ 

983 Create a new `DateTime`. 

984 """ 

985 super(DateTime, self).__init__(self.__class__.ty, **kwargs) 

986 self.format = format 

987 

988 def serialize(self, value): 

989 """ 

990 Serialize the given `~datetime.datetime` as a string. 

991 """ 

992 if self.format == 'iso8601': 

993 return value.isoformat() 

994 else: 

995 return value.strftime(self.format) 

996 

997 def deserialize(self, value): 

998 """ 

999 Deserialize the given string as a `~datetime.datetime`. 

1000 """ 

1001 if self.format == 'iso8601': 

1002 try: 

1003 return self.ty.fromisoformat(value) 

1004 except ValueError: 

1005 raise ValidationError('invalid ISO 8601 datetime', value=value) 

1006 else: 

1007 try: 

1008 return datetime.datetime.strptime(value, self.format) 

1009 except (TypeError, ValueError): 

1010 raise ValidationError( 

1011 f'invalid datetime, expected format {self.format!r}', 

1012 value=value, 

1013 ) 

1014 

1015 

1016class Date(DateTime): 

1017 """ 

1018 A `~datetime.date` field. 

1019 

1020 This field behaves in a similar fashion to the `DateTime` field. 

1021 """ 

1022 

1023 ty = datetime.date 

1024 

1025 def deserialize(self, value): 

1026 """ 

1027 Deserialize the given string as a `~datetime.date`. 

1028 """ 

1029 if self.format == 'iso8601': 

1030 try: 

1031 return self.ty.fromisoformat(value) 

1032 except ValueError: 

1033 raise ValidationError('invalid ISO 8601 date', value=value) 

1034 else: 

1035 try: 

1036 return datetime.datetime.strptime(value, self.format).date() 

1037 except (TypeError, ValueError): 

1038 raise ValidationError( 

1039 f'invalid date, expected format {self.format!r}', 

1040 value=value, 

1041 ) 

1042 

1043 

1044class Time(DateTime): 

1045 """ 

1046 A `~datetime.time` field. 

1047 

1048 This field behaves in a similar fashion to the `DateTime` field. 

1049 """ 

1050 

1051 ty = datetime.time 

1052 

1053 def deserialize(self, value): 

1054 """ 

1055 Deserialize the given string as a `~datetime.time`. 

1056 """ 

1057 if self.format == 'iso8601': 

1058 try: 

1059 return self.ty.fromisoformat(value) 

1060 except ValueError: 

1061 raise ValidationError('invalid ISO 8601 time', value=value) 

1062 else: 

1063 try: 

1064 return datetime.datetime.strptime(value, self.format).time() 

1065 except (TypeError, ValueError): 

1066 raise ValidationError( 

1067 f'invalid time, expected format {self.format!r}', 

1068 value=value, 

1069 ) 

1070 

1071 

1072class Text(Instance): 

1073 """ 

1074 A text field. 

1075 

1076 A `Text` is a string field in Python 3 and a unicode field in Python 2. It 

1077 will normalize byte strings into unicode strings using the given encoding. 

1078 

1079 Args: 

1080 encoding (str): the encoding with which to decode bytes. Passed 

1081 directly to `bytes.decode`. If not given then `chardet.detect` will 

1082 be used to detect the encoding. 

1083 errors (str): The error handling scheme to use for the handling of 

1084 decoding errors. Passed directly to `bytes.decode`. 

1085 **kwargs: keyword arguments for the `Field` constructor. 

1086 """ 

1087 

1088 def __init__(self, encoding=None, errors='strict', **kwargs): 

1089 """ 

1090 Create a new `Text`. 

1091 """ 

1092 super(Text, self).__init__(str, **kwargs) 

1093 self.encoding = encoding 

1094 self.errors = errors 

1095 if self.encoding is None: 

1096 self._detect = try_lookup('chardet.detect') 

1097 

1098 def normalize(self, value): 

1099 """ 

1100 Normalize byte strings to unicode strings. 

1101 """ 

1102 if isinstance(value, bytes): 

1103 if self.encoding is None: 

1104 value = value.decode( 

1105 encoding=self._detect(value)['encoding'], errors=self.errors 

1106 ) 

1107 else: 

1108 value = value.decode(encoding=self.encoding, errors=self.errors) 

1109 

1110 return value 

1111 

1112 

1113class Regex(Text): 

1114 """ 

1115 A regex field. 

1116 

1117 A `Regex` is a string field that validates that data matches a specified 

1118 regex expression. 

1119 

1120 Args: 

1121 pattern (str): the regex pattern that the value must match. 

1122 flags (int): the regex flags passed directly to `re.compile`. 

1123 **kwargs: keyword arguments for the `Field` constructor. 

1124 """ 

1125 

1126 def __init__(self, pattern, flags=0, **kwargs): 

1127 """ 

1128 Create a new `Regex`. 

1129 """ 

1130 super(Regex, self).__init__(**kwargs) 

1131 self.pattern = pattern 

1132 self.flags = flags 

1133 self._compiled = re.compile(pattern, flags=flags) 

1134 

1135 def validate(self, value): 

1136 """ 

1137 Validate the given string matches the specified regex. 

1138 """ 

1139 super(Regex, self).validate(value) 

1140 if not self._compiled.match(value): 

1141 raise ValidationError( 

1142 f'invalid string, expected to match regex pattern {self.pattern!r}', 

1143 value=value, 

1144 ) 

1145 

1146 

1147class Uuid(Instance): 

1148 """ 

1149 A `~uuid.UUID` field. 

1150 

1151 A `Uuid` field validates that the input data is a UUID. It serializes the 

1152 UUID into the specified output form and deserializes hex strings, bytes, 

1153 fields, or integers as UUIDs. 

1154 

1155 Args: 

1156 output_form (str): the type of output form to serialize to. Possible 

1157 values include 'str', 'urn', 'hex', 'int', 'bytes', or 'fields'. 

1158 **kwargs: keyword arguments for the `Field` constructor. 

1159 """ 

1160 

1161 def __init__(self, output_form='str', **kwargs): 

1162 """ 

1163 Create a new `Uuid`. 

1164 """ 

1165 if output_form not in ('str', 'urn', 'hex', 'int', 'bytes', 'fields'): 

1166 raise ValueError('invalid output form') 

1167 super(Uuid, self).__init__(uuid.UUID, **kwargs) 

1168 self.output_form = output_form 

1169 

1170 def serialize(self, value): 

1171 """ 

1172 Serialize the given `~uuid.UUID` as a string. 

1173 """ 

1174 if self.output_form == 'str': 

1175 return str(value) 

1176 else: 

1177 return getattr(value, self.output_form) 

1178 

1179 def normalize(self, value): 

1180 """ 

1181 Normalize the value into a `~uuid.UUID`. 

1182 """ 

1183 if not isinstance(value, uuid.UUID): 

1184 input_form = None 

1185 if isinstance(value, str): 

1186 input_form = 'hex' 

1187 elif isinstance(value, bytes): 

1188 input_form = 'bytes' 

1189 elif isinstance(value, int): 

1190 input_form = 'int' 

1191 elif isinstance(value, (list, tuple)): 

1192 input_form = 'fields' 

1193 if input_form: 

1194 try: 

1195 return uuid.UUID(**{input_form: value}) 

1196 except ValueError: 

1197 pass 

1198 return value 

1199 

1200 

1201class IpAddress(Text): 

1202 """ 

1203 A text field that asserts the text is a valid IP address. 

1204 

1205 The validation is delegated to `validators.ip_address.ipv4` and 

1206 `validators.ip_address.ipv6`. 

1207 

1208 Args: 

1209 **kwargs: keyword arguments for the `Field` constructor. 

1210 """ 

1211 

1212 def __init__(self, **kwargs): 

1213 super(IpAddress, self).__init__(**kwargs) 

1214 self._validator_ipv4 = try_lookup('validators.ip_address.ipv4') 

1215 self._validator_ipv6 = try_lookup('validators.ip_address.ipv6') 

1216 

1217 def validate(self, value): 

1218 super(IpAddress, self).validate(value) 

1219 if not self._validator_ipv4(value) and not self._validator_ipv6(value): 

1220 raise ValidationError('invalid IP address', value=value) 

1221 

1222 

1223def create_from(foreign, name=None, human=None): 

1224 """ 

1225 Create a new `Text` class from a `validators` function. 

1226 """ 

1227 suffix = foreign.split('.', 1)[1] 

1228 

1229 if name is None: 

1230 name = suffix.title() 

1231 if human is None: 

1232 human = suffix 

1233 

1234 doc = f"""A text field that asserts the text is a valid {human}. 

1235 

1236The validation is delegated to `{foreign}`. 

1237 

1238Args: 

1239 **kwargs: keyword arguments for the `Field` constructor. 

1240""" 

1241 

1242 field_cls = type(name, (Text,), {'__doc__': doc}) 

1243 

1244 def __init__(self, **kwargs): # noqa: N807 

1245 super(field_cls, self).__init__(**kwargs) 

1246 self._validator = try_lookup(foreign) 

1247 

1248 def validate(self, value): 

1249 super(field_cls, self).validate(value) 

1250 if not self._validator(value): 

1251 raise ValidationError(f'invalid {human}', value=value) 

1252 

1253 field_cls.__init__ = __init__ 

1254 field_cls.validate = validate 

1255 

1256 return field_cls 

1257 

1258 

1259# Generate string fields using functions in the 'validators' package. 

1260Domain = create_from('validators.domain') 

1261Email = create_from('validators.email') 

1262Ipv4Address = create_from( 

1263 'validators.ip_address.ipv4', name='Ipv4Address', human='IPv4 address' 

1264) 

1265Ipv6Address = create_from( 

1266 'validators.ip_address.ipv6', name='Ipv6Address', human='IPv6 address' 

1267) 

1268MacAddress = create_from( 

1269 'validators.mac_address', name='MacAddress', human='MAC address' 

1270) 

1271Slug = create_from('validators.slug') 

1272Url = create_from('validators.url', human='URL') 

1273 

1274del create_from 

1275 

1276_FIELD_CLASS_MAP = { 

1277 # Built-in types 

1278 bool: Bool, 

1279 bytes: Bytes, 

1280 complex: Complex, 

1281 dict: Dict, 

1282 float: Float, 

1283 frozenset: FrozenSet, 

1284 int: Int, 

1285 list: List, 

1286 set: Set, 

1287 str: Str, 

1288 tuple: Tuple, 

1289 # Collections 

1290 collections.deque: Deque, 

1291 collections.OrderedDict: OrderedDict, 

1292 # Datetimes 

1293 datetime.datetime: DateTime, 

1294 datetime.date: Date, 

1295 datetime.time: Time, 

1296 # Others 

1297 uuid.UUID: Uuid, 

1298 decimal.Decimal: Decimal, 

1299} 

1300 

1301__all__ = [name for name, obj in globals().items() if is_subclass(obj, Field)]