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
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
1"""
2This module contains field classes for use with `Models <serde.Model>`.
3"""
5import collections
6import datetime
7import decimal
8import re
9import uuid
10from collections.abc import Mapping as MappingType
12from serde.exceptions import ContextError, ValidationError, add_context
13from serde.utils import is_subclass, try_lookup, zip_equal
16def _resolve(thing, none_allowed=True):
17 """
18 Resolve an arbitrary object to a `Field` instance.
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`.
25 Returns:
26 Field: a field instance.
27 """
28 from serde.model import Model
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)
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
51 raise TypeError(f'failed to resolve {thing!r} into a field')
54class _Base(object):
55 """
56 A base field or tag on a `~serde.Model`.
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.
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 """
72 # This is so we can get the order the bases were instantiated in.
73 _counter = 1
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 []
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()
90 @property
91 def __model__(self):
92 """
93 The model class that the base field is bound to.
94 """
95 return self._model_cls
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 }
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
117 def _serialize_with(self, model, d):
118 """
119 Serialize value(s) from a model instance to a dictionary.
121 This method needs to be overridden and should use ``self._serialize()``
122 on individual values.
124 Args:
125 model (Model): the model instance that we are serializing from.
126 d (dict): the dictionary to serialize to.
128 Returns:
129 dict: the updated dictionary to continue serializing to.
130 """
131 raise NotImplementedError('this method should be overridden')
133 def _deserialize_with(self, model, d):
134 """
135 Deserialize value(s) from a dictionary to a model instance.
137 This method needs to be overridden and should use
138 ``self._deserialize()`` on individual values.
140 Args:
141 model (Model): the model instance that we are deserializing to.
142 d (dict): the dictionary to deserialize from.
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')
150 def _serialize(self, value):
151 for serializer in self.serializers:
152 value = serializer(value)
153 return self.serialize(value)
155 def _deserialize(self, value):
156 value = self.deserialize(value)
157 for deserializer in self.deserializers:
158 value = deserializer(value)
159 return value
161 def serialize(self, value):
162 """
163 Serialize a value according to this base field's specification.
164 """
165 return value
167 def deserialize(self, value):
168 """
169 Deserialize a value according to this base field's specification.
170 """
171 return value
174class Field(_Base):
175 """
176 A field on a `~serde.Model`.
178 Fields handle serializing, deserializing, normalization, and validation of
179 input values for `~serde.Model` objects.
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 """
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 []
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 }
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
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
250 def _instantiate_with(self, model, kwargs):
251 """
252 Instantiate the corresponding model attribute from the keyword args.
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)
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
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
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))
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))
292 def _instantiate(self, value):
293 return self._default() if value is None else value
295 def _normalize(self, value):
296 value = self.normalize(value)
297 for normalizer in self.normalizers:
298 value = normalizer(value)
299 return value
301 def _validate(self, value):
302 self.validate(value)
303 for validator in self.validators:
304 validator(value)
306 def normalize(self, value):
307 """
308 Normalize a value according to this field's specification.
310 By default this method does not do anything.
311 """
312 return value
314 def validate(self, value):
315 """
316 Validate a value according to this field's specification.
318 By default this method does not do anything.
319 """
320 pass
323class Optional(Field):
324 """
325 An optional field.
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`.
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 """
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)
346 def _instantiate_with(self, model, kwargs):
347 """
348 Instantiate the corresponding model attribute from the keyword args.
350 This method should .pop() from kwargs.
351 """
352 name = self._attr_name
353 setattr(model, name, self._instantiate(kwargs.pop(name, None)))
355 def _serialize_with(self, model, d):
356 """
357 Serialize the corresponding model attribute to a dictionary.
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
366 def _deserialize_with(self, model, d):
367 """
368 Deserialize the corresponding model attribute from a dictionary.
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
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)
387 def _instantiate(self, value):
388 return value
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
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
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
413 def _validate(self, value):
414 if value is not None:
415 self.validate(value)
416 for validator in self.validators:
417 validator(value)
419 def serialize(self, value):
420 """
421 Serialize the given value using the inner `Field`.
422 """
423 return self.inner._serialize(value)
425 def deserialize(self, value):
426 """
427 Deserialize the given value using the inner `Field`.
428 """
429 return self.inner._deserialize(value)
431 def normalize(self, value):
432 """
433 Normalize the given value using the inner `Field`.
434 """
435 return self.inner._normalize(value)
437 def validate(self, value):
438 """
439 Validate the given value using the inner `Field`.
440 """
441 self.inner._validate(value)
444class Instance(Field):
445 """
446 A field that is an instance of a type.
448 An `Instance` field simply validates that the data is the specified type.
450 Args:
451 type: the type that this `Instance` wraps.
452 **kwargs: keyword arguments for the `Field` constructor.
453 """
455 def __init__(self, ty, **kwargs):
456 """
457 Create a new `Instance`.
458 """
459 super(Instance, self).__init__(**kwargs)
460 self.ty = ty
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 )
472class Nested(Instance):
473 """
474 A field for `~serde.Model` fields.
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.
481 Args:
482 model_cls (serde.Model): the nested model class.
483 **kwargs: keyword arguments for the `Field` constructor.
484 """
486 def serialize(self, model):
487 """
488 Serialize the given `~serde.Model` instance as a dictionary.
489 """
490 return model.to_dict()
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)
501class Flatten(Nested):
502 """
503 A field that flattens the serialized version of the wrapped `~serde.Model`
504 into the parent dictionary.
506 This effectively removes one level of structure between the serialized
507 representation and the Python model representation.
509 Warning:
510 this field cannot be contained by another like an `Optional`, or a
511 `List` or `Dict`.
513 Args:
514 model_cls (serde.Model): the nested model class.
515 **kwargs: keyword arguments for the `Field` constructor.
516 """
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
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
533class _Container(Instance):
534 """
535 A base class for `Dict`, `List`, `Tuple`, and other container fields.
536 """
538 def __init__(self, ty, **kwargs):
539 """
540 Create a new `_Container`.
541 """
542 super(_Container, self).__init__(ty, **kwargs)
543 self.kwargs = {}
545 def _iter(self, value):
546 """
547 Iterate over the container.
548 """
549 raise NotImplementedError()
551 def _apply(self, stage, element):
552 """
553 Apply a stage to a particular element in the container.
554 """
555 raise NotImplementedError()
557 def serialize(self, value):
558 """
559 Serialize the given container.
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)
570 def deserialize(self, value):
571 """
572 Deserialize the given container.
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 )
583 def normalize(self, value):
584 """
585 Deserialize the given container.
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 )
596 def validate(self, value):
597 """
598 Validate the given container.
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)
608class _Mapping(_Container):
609 """
610 A mapping field to be used as the base class for `Dict` and `OrderedDict`.
611 """
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)
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 )
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))
639class Dict(_Mapping):
640 """
641 This field represents the built-in `dict` type.
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 """
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)
656class OrderedDict(_Mapping):
657 """
658 An `~collections.OrderedDict` field.
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 """
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 )
675class _Sequence(_Container):
676 """
677 A sequence field to be used as the base class for fields such as `List` and `Set`
678 """
680 def __init__(self, ty, element=None, **kwargs):
681 super(_Sequence, self).__init__(ty, **kwargs)
682 self.element = _resolve(element)
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 )
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)
705class Deque(_Sequence):
706 """
707 A `~collections.deque` field.
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 """
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}
722 @property
723 def maxlen(self):
724 """
725 The maximum length of this `Deque`.
726 """
727 return self.kwargs['maxlen']
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 )
740class FrozenSet(_Sequence):
741 """
742 This field represents the built-in `frozenset` type.
744 Args:
745 element: the `Field` class or instance for elements in the `Set`.
746 **kwargs: keyword arguments for the `Field` constructor.
747 """
749 def __init__(self, element=None, **kwargs):
750 """
751 Create a new `FrozenSet`.
752 """
753 super(FrozenSet, self).__init__(frozenset, element=element, **kwargs)
756class List(_Sequence):
757 """
758 This field represents the built-in `list` type.
760 Args:
761 element: the `Field` class or instance for elements in the `List`.
762 **kwargs: keyword arguments for the `Field` constructor.
763 """
765 def __init__(self, element=None, **kwargs):
766 """
767 Create a new `List`.
768 """
769 super(List, self).__init__(list, element=element, **kwargs)
772class Set(_Sequence):
773 """
774 This field represents the built-in `set` type.
776 Args:
777 element: the `Field` class or instance for elements in the `Set`.
778 **kwargs: keyword arguments for the `Field` constructor.
779 """
781 def __init__(self, element=None, **kwargs):
782 """
783 Create a new `Set`.
784 """
785 super(Set, self).__init__(set, element=element, **kwargs)
788class Tuple(_Sequence):
789 """
790 This field represents the built-in `tuple` type.
792 Args:
793 *elements: the `Field` classes or instances for elements in this tuple.
794 **kwargs: keyword arguments for the `Field` constructor.
795 """
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)
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 )
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)
826def create_primitive(name, ty):
827 """
828 Create a primitive `Field` class.
829 """
830 doc = f"""This field represents the built-in `{ty}` type.
832Args:
833 **kwargs: keyword arguments for the `Field` constructor.
834"""
836 def __init__(self, **kwargs): # noqa: N807
837 Instance.__init__(self, ty, **kwargs)
839 __init__.__doc__ = f'Create a new `{name}`.'
841 return type(name, (Instance,), {'__doc__': doc, '__init__': __init__})
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)
851del create_primitive
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()
861class Decimal(Instance):
862 """
863 A `~decimal.Decimal` field.
865 This field serializes `~decimal.Decimal` objects as strings and
866 deserializes string representations of Decimals as `~decimal.Decimal`
867 objects.
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.
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
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 """
884 ty = decimal.Decimal
886 def __init__(self, resolution=None, **kwargs):
887 super(Decimal, self).__init__(self.__class__.ty, **kwargs)
888 self.resolution = resolution
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)
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 )
902 return decimal.Decimal(value)
903 except decimal.DecimalException:
904 raise ValidationError('invalid decimal', value=value)
907class Literal(Field):
908 """
909 A literal field.
911 A `Literal` is a field that always has to be the specified value.
913 Args:
914 value: the value that this `Literal` wraps.
915 **kwargs: keyword arguments for the `Field` constructor.
916 """
918 def __init__(self, value, **kwargs):
919 """
920 Create a new `Literal`.
921 """
922 super(Literal, self).__init__(**kwargs)
923 self.value = value
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 )
935class Choice(Field):
936 """
937 One of a given selection of values.
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.
942 Args:
943 choices: a list or range or tuple of allowed values.
944 **kwargs: keyword arguments for the `Field` constructor.
945 """
947 def __init__(self, choices, **kwargs):
948 """
949 Create a new `Choice`.
950 """
951 super(Choice, self).__init__(**kwargs)
952 self.choices = choices
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)
963class DateTime(Instance):
964 """
965 A `~datetime.datetime` field.
967 This field serializes `~datetime.datetime` objects as strings and
968 deserializes string representations of datetimes as `~datetime.datetime`
969 objects.
971 The date format can be specified. It will default to ISO 8601.
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 """
979 ty = datetime.datetime
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
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)
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 )
1016class Date(DateTime):
1017 """
1018 A `~datetime.date` field.
1020 This field behaves in a similar fashion to the `DateTime` field.
1021 """
1023 ty = datetime.date
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 )
1044class Time(DateTime):
1045 """
1046 A `~datetime.time` field.
1048 This field behaves in a similar fashion to the `DateTime` field.
1049 """
1051 ty = datetime.time
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 )
1072class Text(Instance):
1073 """
1074 A text field.
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.
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 """
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')
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)
1110 return value
1113class Regex(Text):
1114 """
1115 A regex field.
1117 A `Regex` is a string field that validates that data matches a specified
1118 regex expression.
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 """
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)
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 )
1147class Uuid(Instance):
1148 """
1149 A `~uuid.UUID` field.
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.
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 """
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
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)
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
1201class IpAddress(Text):
1202 """
1203 A text field that asserts the text is a valid IP address.
1205 The validation is delegated to `validators.ip_address.ipv4` and
1206 `validators.ip_address.ipv6`.
1208 Args:
1209 **kwargs: keyword arguments for the `Field` constructor.
1210 """
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')
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)
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]
1229 if name is None:
1230 name = suffix.title()
1231 if human is None:
1232 human = suffix
1234 doc = f"""A text field that asserts the text is a valid {human}.
1236The validation is delegated to `{foreign}`.
1238Args:
1239 **kwargs: keyword arguments for the `Field` constructor.
1240"""
1242 field_cls = type(name, (Text,), {'__doc__': doc})
1244 def __init__(self, **kwargs): # noqa: N807
1245 super(field_cls, self).__init__(**kwargs)
1246 self._validator = try_lookup(foreign)
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)
1253 field_cls.__init__ = __init__
1254 field_cls.validate = validate
1256 return field_cls
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')
1274del create_from
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}
1301__all__ = [name for name, obj in globals().items() if is_subclass(obj, Field)]