aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/marshmallow/fields.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/marshmallow/fields.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/marshmallow/fields.py')
-rw-r--r--.venv/lib/python3.12/site-packages/marshmallow/fields.py2153
1 files changed, 2153 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/marshmallow/fields.py b/.venv/lib/python3.12/site-packages/marshmallow/fields.py
new file mode 100644
index 00000000..dc3d8120
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/marshmallow/fields.py
@@ -0,0 +1,2153 @@
+# ruff: noqa: F841, SLF001
+from __future__ import annotations
+
+import collections
+import copy
+import datetime as dt
+import decimal
+import ipaddress
+import math
+import numbers
+import typing
+import uuid
+import warnings
+from collections.abc import Mapping as _Mapping
+
+from marshmallow import class_registry, types, utils, validate
+from marshmallow.base import FieldABC
+from marshmallow.exceptions import (
+ FieldInstanceResolutionError,
+ StringNotCollectionError,
+ ValidationError,
+)
+from marshmallow.utils import (
+ is_aware,
+ is_collection,
+ resolve_field_instance,
+)
+from marshmallow.utils import (
+ missing as missing_,
+)
+from marshmallow.validate import And, Length
+from marshmallow.warnings import (
+ ChangedInMarshmallow4Warning,
+ RemovedInMarshmallow4Warning,
+)
+
+if typing.TYPE_CHECKING:
+ from enum import Enum as EnumType
+
+ from marshmallow.schema import Schema, SchemaMeta
+
+
+__all__ = [
+ "IP",
+ "URL",
+ "UUID",
+ "AwareDateTime",
+ "Bool",
+ "Boolean",
+ "Constant",
+ "Date",
+ "DateTime",
+ "Decimal",
+ "Dict",
+ "Email",
+ "Enum",
+ "Field",
+ "Float",
+ "Function",
+ "IPInterface",
+ "IPv4",
+ "IPv4Interface",
+ "IPv6",
+ "IPv6Interface",
+ "Int",
+ "Integer",
+ "List",
+ "Mapping",
+ "Method",
+ "NaiveDateTime",
+ "Nested",
+ "Number",
+ "Pluck",
+ "Raw",
+ "Str",
+ "String",
+ "Time",
+ "TimeDelta",
+ "Tuple",
+ "Url",
+]
+
+
+class Field(FieldABC):
+ """Base field from which other fields inherit.
+
+ :param dump_default: If set, this value will be used during serialization if the
+ input value is missing. If not set, the field will be excluded from the
+ serialized output if the input value is missing. May be a value or a callable.
+ :param load_default: Default deserialization value for the field if the field is not
+ found in the input data. May be a value or a callable.
+ :param data_key: The name of the dict key in the external representation, i.e.
+ the input of `load` and the output of `dump`.
+ If `None`, the key will match the name of the field.
+ :param attribute: The name of the key/attribute in the internal representation, i.e.
+ the output of `load` and the input of `dump`.
+ If `None`, the key/attribute will match the name of the field.
+ Note: This should only be used for very specific use cases such as
+ outputting multiple fields for a single attribute, or using keys/attributes
+ that are invalid variable names, unsuitable for field names. In most cases,
+ you should use ``data_key`` instead.
+ :param validate: Validator or collection of validators that are called
+ during deserialization. Validator takes a field's input value as
+ its only parameter and returns a boolean.
+ If it returns `False`, an :exc:`ValidationError` is raised.
+ :param required: Raise a :exc:`ValidationError` if the field value
+ is not supplied during deserialization.
+ :param allow_none: Set this to `True` if `None` should be considered a valid value during
+ validation/deserialization. If set to `False` (the default), `None` is considered invalid input.
+ If ``load_default`` is explicitly set to `None` and ``allow_none`` is unset,
+ `allow_none` is implicitly set to ``True``.
+ :param load_only: If `True` skip this field during serialization, otherwise
+ its value will be present in the serialized data.
+ :param dump_only: If `True` skip this field during deserialization, otherwise
+ its value will be present in the deserialized object. In the context of an
+ HTTP API, this effectively marks the field as "read-only".
+ :param error_messages: Overrides for `Field.default_error_messages`.
+ :param metadata: Extra information to be stored as field metadata.
+
+ .. versionchanged:: 3.0.0b8
+ Add ``data_key`` parameter for the specifying the key in the input and
+ output data. This parameter replaced both ``load_from`` and ``dump_to``.
+
+ .. versionchanged:: 3.13.0
+ Replace ``missing`` and ``default`` parameters with ``load_default`` and ``dump_default``.
+
+ .. versionchanged:: 3.24.0
+ `Field <marshmallow.fields.Field>` should no longer be used as a field within a `Schema <marshmallow.Schema>`.
+ Use `Raw <marshmallow.fields.Raw>` or another `Field <marshmallow.fields.Field>` subclass instead.
+ """
+
+ # Some fields, such as Method fields and Function fields, are not expected
+ # to exist as attributes on the objects to serialize. Set this to False
+ # for those fields
+ _CHECK_ATTRIBUTE = True
+
+ #: Default error messages for various kinds of errors. The keys in this dictionary
+ #: are passed to `Field.make_error`. The values are error messages passed to
+ #: :exc:`marshmallow.exceptions.ValidationError`.
+ default_error_messages: dict[str, str] = {
+ "required": "Missing data for required field.",
+ "null": "Field may not be null.",
+ "validator_failed": "Invalid value.",
+ }
+
+ def __init__(
+ self,
+ *,
+ load_default: typing.Any = missing_,
+ missing: typing.Any = missing_,
+ dump_default: typing.Any = missing_,
+ default: typing.Any = missing_,
+ data_key: str | None = None,
+ attribute: str | None = None,
+ validate: types.Validator | typing.Iterable[types.Validator] | None = None,
+ required: bool = False,
+ allow_none: bool | None = None,
+ load_only: bool = False,
+ dump_only: bool = False,
+ error_messages: dict[str, str] | None = None,
+ metadata: typing.Mapping[str, typing.Any] | None = None,
+ **additional_metadata,
+ ) -> None:
+ if self.__class__ is Field:
+ warnings.warn(
+ "`Field` should not be instantiated. Use `fields.Raw` or "
+ "another field subclass instead.",
+ ChangedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ # handle deprecated `default` and `missing` parameters
+ if default is not missing_:
+ warnings.warn(
+ "The 'default' argument to fields is deprecated. "
+ "Use 'dump_default' instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ if dump_default is missing_:
+ dump_default = default
+ if missing is not missing_:
+ warnings.warn(
+ "The 'missing' argument to fields is deprecated. "
+ "Use 'load_default' instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ if load_default is missing_:
+ load_default = missing
+ self.dump_default = dump_default
+ self.load_default = load_default
+
+ self.attribute = attribute
+ self.data_key = data_key
+ self.validate = validate
+ if validate is None:
+ self.validators = []
+ elif callable(validate):
+ self.validators = [validate]
+ elif utils.is_iterable_but_not_string(validate):
+ self.validators = list(validate)
+ else:
+ raise ValueError(
+ "The 'validate' parameter must be a callable "
+ "or a collection of callables."
+ )
+
+ # If allow_none is None and load_default is None
+ # None should be considered valid by default
+ self.allow_none = load_default is None if allow_none is None else allow_none
+ self.load_only = load_only
+ self.dump_only = dump_only
+ if required is True and load_default is not missing_:
+ raise ValueError("'load_default' must not be set for required fields.")
+ self.required = required
+
+ metadata = metadata or {}
+ self.metadata = {**metadata, **additional_metadata}
+ if additional_metadata:
+ warnings.warn(
+ "Passing field metadata as keyword arguments is deprecated. Use the "
+ "explicit `metadata=...` argument instead. "
+ f"Additional metadata: {additional_metadata}",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+
+ # Collect default error message from self and parent classes
+ messages: dict[str, str] = {}
+ for cls in reversed(self.__class__.__mro__):
+ messages.update(getattr(cls, "default_error_messages", {}))
+ messages.update(error_messages or {})
+ self.error_messages = messages
+
+ self.parent: Field | Schema | None = None
+ self.name: str | None = None
+ self.root: Schema | None = None
+
+ def __repr__(self) -> str:
+ return (
+ f"<fields.{self.__class__.__name__}(dump_default={self.dump_default!r}, "
+ f"attribute={self.attribute!r}, "
+ f"validate={self.validate}, required={self.required}, "
+ f"load_only={self.load_only}, dump_only={self.dump_only}, "
+ f"load_default={self.load_default}, allow_none={self.allow_none}, "
+ f"error_messages={self.error_messages})>"
+ )
+
+ def __deepcopy__(self, memo):
+ return copy.copy(self)
+
+ def get_value(
+ self,
+ obj: typing.Any,
+ attr: str,
+ accessor: (
+ typing.Callable[[typing.Any, str, typing.Any], typing.Any] | None
+ ) = None,
+ default: typing.Any = missing_,
+ ):
+ """Return the value for a given key from an object.
+
+ :param obj: The object to get the value from.
+ :param attr: The attribute/key in `obj` to get the value from.
+ :param accessor: A callable used to retrieve the value of `attr` from
+ the object `obj`. Defaults to `marshmallow.utils.get_value`.
+ """
+ accessor_func = accessor or utils.get_value
+ check_key = attr if self.attribute is None else self.attribute
+ return accessor_func(obj, check_key, default)
+
+ def _validate(self, value: typing.Any):
+ """Perform validation on ``value``. Raise a :exc:`ValidationError` if validation
+ does not succeed.
+ """
+ self._validate_all(value)
+
+ @property
+ def _validate_all(self) -> typing.Callable[[typing.Any], None]:
+ return And(*self.validators, error=self.error_messages["validator_failed"])
+
+ def make_error(self, key: str, **kwargs) -> ValidationError:
+ """Helper method to make a `ValidationError` with an error message
+ from ``self.error_messages``.
+ """
+ try:
+ msg = self.error_messages[key]
+ except KeyError as error:
+ class_name = self.__class__.__name__
+ message = (
+ f"ValidationError raised by `{class_name}`, but error key `{key}` does "
+ "not exist in the `error_messages` dictionary."
+ )
+ raise AssertionError(message) from error
+ if isinstance(msg, (str, bytes)):
+ msg = msg.format(**kwargs)
+ return ValidationError(msg)
+
+ def fail(self, key: str, **kwargs):
+ """Helper method that raises a `ValidationError` with an error message
+ from ``self.error_messages``.
+
+ .. deprecated:: 3.0.0
+ Use `make_error <marshmallow.fields.Field.make_error>` instead.
+ """
+ warnings.warn(
+ f'`Field.fail` is deprecated. Use `raise self.make_error("{key}", ...)` instead.',
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ raise self.make_error(key=key, **kwargs)
+
+ def _validate_missing(self, value: typing.Any) -> None:
+ """Validate missing values. Raise a :exc:`ValidationError` if
+ `value` should be considered missing.
+ """
+ if value is missing_ and self.required:
+ raise self.make_error("required")
+ if value is None and not self.allow_none:
+ raise self.make_error("null")
+
+ def serialize(
+ self,
+ attr: str,
+ obj: typing.Any,
+ accessor: (
+ typing.Callable[[typing.Any, str, typing.Any], typing.Any] | None
+ ) = None,
+ **kwargs,
+ ):
+ """Pulls the value for the given key from the object, applies the
+ field's formatting and returns the result.
+
+ :param attr: The attribute/key to get from the object.
+ :param obj: The object to access the attribute/key from.
+ :param accessor: Function used to access values from ``obj``.
+ :param kwargs: Field-specific keyword arguments.
+ """
+ if self._CHECK_ATTRIBUTE:
+ value = self.get_value(obj, attr, accessor=accessor)
+ if value is missing_:
+ default = self.dump_default
+ value = default() if callable(default) else default
+ if value is missing_:
+ return value
+ else:
+ value = None
+ return self._serialize(value, attr, obj, **kwargs)
+
+ def deserialize(
+ self,
+ value: typing.Any,
+ attr: str | None = None,
+ data: typing.Mapping[str, typing.Any] | None = None,
+ **kwargs,
+ ):
+ """Deserialize ``value``.
+
+ :param value: The value to deserialize.
+ :param attr: The attribute/key in `data` to deserialize.
+ :param data: The raw input data passed to `Schema.load <marshmallow.Schema.load>`.
+ :param kwargs: Field-specific keyword arguments.
+ :raise ValidationError: If an invalid value is passed or if a required value
+ is missing.
+ """
+ # Validate required fields, deserialize, then validate
+ # deserialized value
+ self._validate_missing(value)
+ if value is missing_:
+ _miss = self.load_default
+ return _miss() if callable(_miss) else _miss
+ if self.allow_none and value is None:
+ return None
+ output = self._deserialize(value, attr, data, **kwargs)
+ self._validate(output)
+ return output
+
+ # Methods for concrete classes to override.
+
+ def _bind_to_schema(self, field_name: str, schema: Schema | Field) -> None:
+ """Update field with values from its parent schema. Called by
+ `Schema._bind_field <marshmallow.Schema._bind_field>`.
+
+ :param field_name: Field name set in schema.
+ :param schema: Parent object.
+ """
+ self.parent = self.parent or schema
+ self.name = self.name or field_name
+ self.root = self.root or (
+ self.parent.root if isinstance(self.parent, FieldABC) else self.parent
+ )
+
+ def _serialize(
+ self, value: typing.Any, attr: str | None, obj: typing.Any, **kwargs
+ ) -> typing.Any:
+ """Serializes ``value`` to a basic Python datatype. Noop by default.
+ Concrete :class:`Field` classes should implement this method.
+
+ Example: ::
+
+ class TitleCase(Field):
+ def _serialize(self, value, attr, obj, **kwargs):
+ if not value:
+ return ""
+ return str(value).title()
+
+ :param value: The value to be serialized.
+ :param attr: The attribute or key on the object to be serialized.
+ :param obj: The object the value was pulled from.
+ :param kwargs: Field-specific keyword arguments.
+ :return: The serialized value
+ """
+ return value
+
+ def _deserialize(
+ self,
+ value: typing.Any,
+ attr: str | None,
+ data: typing.Mapping[str, typing.Any] | None,
+ **kwargs,
+ ) -> typing.Any:
+ """Deserialize value. Concrete :class:`Field` classes should implement this method.
+
+ :param value: The value to be deserialized.
+ :param attr: The attribute/key in `data` to be deserialized.
+ :param data: The raw input data passed to the `Schema.load <marshmallow.Schema.load>`.
+ :param kwargs: Field-specific keyword arguments.
+ :raise ValidationError: In case of formatting or validation failure.
+ :return: The deserialized value.
+
+ .. versionchanged:: 3.0.0
+ Added ``**kwargs`` to signature.
+ """
+ return value
+
+ # Properties
+
+ @property
+ def context(self) -> dict | None:
+ """The context dictionary for the parent `Schema <marshmallow.Schema>`."""
+ if self.parent:
+ return self.parent.context
+ return None
+
+ # the default and missing properties are provided for compatibility and
+ # emit warnings when they are accessed and set
+ @property
+ def default(self):
+ warnings.warn(
+ "The 'default' attribute of fields is deprecated. "
+ "Use 'dump_default' instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ return self.dump_default
+
+ @default.setter
+ def default(self, value):
+ warnings.warn(
+ "The 'default' attribute of fields is deprecated. "
+ "Use 'dump_default' instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ self.dump_default = value
+
+ @property
+ def missing(self):
+ warnings.warn(
+ "The 'missing' attribute of fields is deprecated. "
+ "Use 'load_default' instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ return self.load_default
+
+ @missing.setter
+ def missing(self, value):
+ warnings.warn(
+ "The 'missing' attribute of fields is deprecated. "
+ "Use 'load_default' instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ self.load_default = value
+
+
+class Raw(Field):
+ """Field that applies no formatting."""
+
+
+class Nested(Field):
+ """Allows you to nest a :class:`Schema <marshmallow.Schema>`
+ inside a field.
+
+ Examples: ::
+
+ class ChildSchema(Schema):
+ id = fields.Str()
+ name = fields.Str()
+ # Use lambda functions when you need two-way nesting or self-nesting
+ parent = fields.Nested(lambda: ParentSchema(only=("id",)), dump_only=True)
+ siblings = fields.List(
+ fields.Nested(lambda: ChildSchema(only=("id", "name")))
+ )
+
+
+ class ParentSchema(Schema):
+ id = fields.Str()
+ children = fields.List(
+ fields.Nested(ChildSchema(only=("id", "parent", "siblings")))
+ )
+ spouse = fields.Nested(lambda: ParentSchema(only=("id",)))
+
+ When passing a `Schema <marshmallow.Schema>` instance as the first argument,
+ the instance's ``exclude``, ``only``, and ``many`` attributes will be respected.
+
+ Therefore, when passing the ``exclude``, ``only``, or ``many`` arguments to `fields.Nested`,
+ you should pass a `Schema <marshmallow.Schema>` class (not an instance) as the first argument.
+
+ ::
+
+ # Yes
+ author = fields.Nested(UserSchema, only=("id", "name"))
+
+ # No
+ author = fields.Nested(UserSchema(), only=("id", "name"))
+
+ :param nested: `Schema <marshmallow.Schema>` instance, class, class name (string), dictionary, or callable that
+ returns a `Schema <marshmallow.Schema>` or dictionary.
+ Dictionaries are converted with `Schema.from_dict <marshmallow.Schema.from_dict>`.
+ :param exclude: A list or tuple of fields to exclude.
+ :param only: A list or tuple of fields to marshal. If `None`, all fields are marshalled.
+ This parameter takes precedence over ``exclude``.
+ :param many: Whether the field is a collection of objects.
+ :param unknown: Whether to exclude, include, or raise an error for unknown
+ fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+ """
+
+ #: Default error messages.
+ default_error_messages = {"type": "Invalid type."}
+
+ def __init__(
+ self,
+ nested: (
+ Schema
+ | SchemaMeta
+ | str
+ | dict[str, Field]
+ | typing.Callable[[], Schema | SchemaMeta | dict[str, Field]]
+ ),
+ *,
+ dump_default: typing.Any = missing_,
+ default: typing.Any = missing_,
+ only: types.StrSequenceOrSet | None = None,
+ exclude: types.StrSequenceOrSet = (),
+ many: bool = False,
+ unknown: str | None = None,
+ **kwargs,
+ ):
+ # Raise error if only or exclude is passed as string, not list of strings
+ if only is not None and not is_collection(only):
+ raise StringNotCollectionError('"only" should be a collection of strings.')
+ if not is_collection(exclude):
+ raise StringNotCollectionError(
+ '"exclude" should be a collection of strings.'
+ )
+ if nested == "self":
+ warnings.warn(
+ "Passing 'self' to `Nested` is deprecated. "
+ "Use `Nested(lambda: MySchema(...))` instead.",
+ RemovedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ self.nested = nested
+ self.only = only
+ self.exclude = exclude
+ self.many = many
+ self.unknown = unknown
+ self._schema: Schema | None = None # Cached Schema instance
+ super().__init__(default=default, dump_default=dump_default, **kwargs)
+
+ @property
+ def schema(self) -> Schema:
+ """The nested `Schema <marshmallow.Schema>` object.
+
+ .. versionchanged:: 1.0.0
+ Renamed from ``serializer`` to ``schema``.
+ """
+ if not self._schema:
+ # Inherit context from parent.
+ context = getattr(self.parent, "context", {})
+ if callable(self.nested) and not isinstance(self.nested, type):
+ nested = self.nested()
+ else:
+ nested = typing.cast("Schema", self.nested)
+ # defer the import of `marshmallow.schema` to avoid circular imports
+ from marshmallow.schema import Schema
+
+ if isinstance(nested, dict):
+ nested = Schema.from_dict(nested)
+
+ if isinstance(nested, Schema):
+ self._schema = copy.copy(nested)
+ self._schema.context.update(context)
+ # Respect only and exclude passed from parent and re-initialize fields
+ set_class = typing.cast(type[set], self._schema.set_class)
+ if self.only is not None:
+ if self._schema.only is not None:
+ original = self._schema.only
+ else: # only=None -> all fields
+ original = self._schema.fields.keys()
+ self._schema.only = set_class(self.only) & set_class(original)
+ if self.exclude:
+ original = self._schema.exclude
+ self._schema.exclude = set_class(self.exclude) | set_class(original)
+ self._schema._init_fields()
+ else:
+ if isinstance(nested, type) and issubclass(nested, Schema):
+ schema_class: type[Schema] = nested
+ elif not isinstance(nested, (str, bytes)):
+ raise ValueError(
+ "`Nested` fields must be passed a "
+ f"`Schema`, not {nested.__class__}."
+ )
+ elif nested == "self":
+ schema_class = typing.cast(Schema, self.root).__class__
+ else:
+ schema_class = class_registry.get_class(nested, all=False)
+ self._schema = schema_class(
+ many=self.many,
+ only=self.only,
+ exclude=self.exclude,
+ context=context,
+ load_only=self._nested_normalized_option("load_only"),
+ dump_only=self._nested_normalized_option("dump_only"),
+ )
+ return self._schema
+
+ def _nested_normalized_option(self, option_name: str) -> list[str]:
+ nested_field = f"{self.name}."
+ return [
+ field.split(nested_field, 1)[1]
+ for field in getattr(self.root, option_name, set())
+ if field.startswith(nested_field)
+ ]
+
+ def _serialize(self, nested_obj, attr, obj, **kwargs):
+ # Load up the schema first. This allows a RegistryError to be raised
+ # if an invalid schema name was passed
+ schema = self.schema
+ if nested_obj is None:
+ return None
+ many = schema.many or self.many
+ return schema.dump(nested_obj, many=many)
+
+ def _test_collection(self, value: typing.Any) -> None:
+ many = self.schema.many or self.many
+ if many and not utils.is_collection(value):
+ raise self.make_error("type", input=value, type=value.__class__.__name__)
+
+ def _load(
+ self, value: typing.Any, partial: bool | types.StrSequenceOrSet | None = None
+ ):
+ try:
+ valid_data = self.schema.load(value, unknown=self.unknown, partial=partial)
+ except ValidationError as error:
+ raise ValidationError(
+ error.messages, valid_data=error.valid_data
+ ) from error
+ return valid_data
+
+ def _deserialize(
+ self,
+ value: typing.Any,
+ attr: str | None,
+ data: typing.Mapping[str, typing.Any] | None,
+ partial: bool | types.StrSequenceOrSet | None = None,
+ **kwargs,
+ ) -> typing.Any:
+ """Same as :meth:`Field._deserialize` with additional ``partial`` argument.
+
+ :param partial: For nested schemas, the ``partial``
+ parameter passed to `marshmallow.Schema.load`.
+
+ .. versionchanged:: 3.0.0
+ Add ``partial`` parameter.
+ """
+ self._test_collection(value)
+ return self._load(value, partial=partial)
+
+
+class Pluck(Nested):
+ """Allows you to replace nested data with one of the data's fields.
+
+ Example: ::
+
+ from marshmallow import Schema, fields
+
+
+ class ArtistSchema(Schema):
+ id = fields.Int()
+ name = fields.Str()
+
+
+ class AlbumSchema(Schema):
+ artist = fields.Pluck(ArtistSchema, "id")
+
+
+ in_data = {"artist": 42}
+ loaded = AlbumSchema().load(in_data) # => {'artist': {'id': 42}}
+ dumped = AlbumSchema().dump(loaded) # => {'artist': 42}
+
+ :param nested: The Schema class or class name (string)
+ to nest, or ``"self"`` to nest the `Schema <marshmallow.Schema>` within itself.
+ :param field_name: The key to pluck a value from.
+ :param kwargs: The same keyword arguments that :class:`Nested` receives.
+ """
+
+ def __init__(
+ self,
+ nested: Schema | SchemaMeta | str | typing.Callable[[], Schema],
+ field_name: str,
+ *,
+ many: bool = False,
+ unknown: str | None = None,
+ **kwargs,
+ ):
+ super().__init__(
+ nested, only=(field_name,), many=many, unknown=unknown, **kwargs
+ )
+ self.field_name = field_name
+
+ @property
+ def _field_data_key(self) -> str:
+ only_field = self.schema.fields[self.field_name]
+ return only_field.data_key or self.field_name
+
+ def _serialize(self, nested_obj, attr, obj, **kwargs):
+ ret = super()._serialize(nested_obj, attr, obj, **kwargs)
+ if ret is None:
+ return None
+ if self.many:
+ return utils.pluck(ret, key=self._field_data_key)
+ return ret[self._field_data_key]
+
+ def _deserialize(self, value, attr, data, partial=None, **kwargs):
+ self._test_collection(value)
+ if self.many:
+ value = [{self._field_data_key: v} for v in value]
+ else:
+ value = {self._field_data_key: value}
+ return self._load(value, partial=partial)
+
+
+class List(Field):
+ """A list field, composed with another `Field` class or
+ instance.
+
+ Example: ::
+
+ numbers = fields.List(fields.Float())
+
+ :param cls_or_instance: A field class or instance.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. versionchanged:: 3.0.0rc9
+ Does not serialize scalar values to single-item lists.
+ """
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid list."}
+
+ def __init__(self, cls_or_instance: Field | type[Field], **kwargs):
+ super().__init__(**kwargs)
+ try:
+ self.inner = resolve_field_instance(cls_or_instance)
+ except FieldInstanceResolutionError as error:
+ raise ValueError(
+ "The list elements must be a subclass or instance of "
+ "marshmallow.base.FieldABC."
+ ) from error
+ if isinstance(self.inner, Nested):
+ self.only = self.inner.only
+ self.exclude = self.inner.exclude
+
+ def _bind_to_schema(self, field_name: str, schema: Schema | Field) -> None:
+ super()._bind_to_schema(field_name, schema)
+ self.inner = copy.deepcopy(self.inner)
+ self.inner._bind_to_schema(field_name, self)
+ if isinstance(self.inner, Nested):
+ self.inner.only = self.only
+ self.inner.exclude = self.exclude
+
+ def _serialize(self, value, attr, obj, **kwargs) -> list[typing.Any] | None:
+ if value is None:
+ return None
+ return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
+
+ def _deserialize(self, value, attr, data, **kwargs) -> list[typing.Any]:
+ if not utils.is_collection(value):
+ raise self.make_error("invalid")
+
+ result = []
+ errors = {}
+ for idx, each in enumerate(value):
+ try:
+ result.append(self.inner.deserialize(each, **kwargs))
+ except ValidationError as error:
+ if error.valid_data is not None:
+ result.append(error.valid_data)
+ errors.update({idx: error.messages})
+ if errors:
+ raise ValidationError(errors, valid_data=result)
+ return result
+
+
+class Tuple(Field):
+ """A tuple field, composed of a fixed number of other `Field` classes or
+ instances
+
+ Example: ::
+
+ row = Tuple((fields.String(), fields.Integer(), fields.Float()))
+
+ .. note::
+ Because of the structured nature of `collections.namedtuple` and
+ `typing.NamedTuple`, using a Schema within a Nested field for them is
+ more appropriate than using a `Tuple` field.
+
+ :param tuple_fields: An iterable of field classes or
+ instances.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. versionadded:: 3.0.0rc4
+ """
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid tuple."}
+
+ def __init__(
+ self,
+ tuple_fields: typing.Iterable[Field] | typing.Iterable[type[Field]],
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+ if not utils.is_collection(tuple_fields):
+ raise ValueError(
+ "tuple_fields must be an iterable of Field classes or instances."
+ )
+
+ try:
+ self.tuple_fields = [
+ resolve_field_instance(cls_or_instance)
+ for cls_or_instance in tuple_fields
+ ]
+ except FieldInstanceResolutionError as error:
+ raise ValueError(
+ 'Elements of "tuple_fields" must be subclasses or '
+ "instances of marshmallow.base.FieldABC."
+ ) from error
+
+ self.validate_length = Length(equal=len(self.tuple_fields))
+
+ def _bind_to_schema(self, field_name: str, schema: Schema | Field) -> None:
+ super()._bind_to_schema(field_name, schema)
+ new_tuple_fields = []
+ for field in self.tuple_fields:
+ new_field = copy.deepcopy(field)
+ new_field._bind_to_schema(field_name, self)
+ new_tuple_fields.append(new_field)
+
+ self.tuple_fields = new_tuple_fields
+
+ def _serialize(self, value, attr, obj, **kwargs) -> tuple | None:
+ if value is None:
+ return None
+
+ return tuple(
+ field._serialize(each, attr, obj, **kwargs)
+ for field, each in zip(self.tuple_fields, value)
+ )
+
+ def _deserialize(self, value, attr, data, **kwargs) -> tuple:
+ if not utils.is_collection(value):
+ raise self.make_error("invalid")
+
+ self.validate_length(value)
+
+ result = []
+ errors = {}
+
+ for idx, (field, each) in enumerate(zip(self.tuple_fields, value)):
+ try:
+ result.append(field.deserialize(each, **kwargs))
+ except ValidationError as error:
+ if error.valid_data is not None:
+ result.append(error.valid_data)
+ errors.update({idx: error.messages})
+ if errors:
+ raise ValidationError(errors, valid_data=result)
+
+ return tuple(result)
+
+
+class String(Field):
+ """A string field.
+
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+ """
+
+ #: Default error messages.
+ default_error_messages = {
+ "invalid": "Not a valid string.",
+ "invalid_utf8": "Not a valid utf-8 string.",
+ }
+
+ def _serialize(self, value, attr, obj, **kwargs) -> str | None:
+ if value is None:
+ return None
+ return utils.ensure_text_type(value)
+
+ def _deserialize(self, value, attr, data, **kwargs) -> typing.Any:
+ if not isinstance(value, (str, bytes)):
+ raise self.make_error("invalid")
+ try:
+ return utils.ensure_text_type(value)
+ except UnicodeDecodeError as error:
+ raise self.make_error("invalid_utf8") from error
+
+
+class UUID(String):
+ """A UUID field."""
+
+ #: Default error messages.
+ default_error_messages = {"invalid_uuid": "Not a valid UUID."}
+
+ def _validated(self, value) -> uuid.UUID | None:
+ """Format the value or raise a :exc:`ValidationError` if an error occurs."""
+ if value is None:
+ return None
+ if isinstance(value, uuid.UUID):
+ return value
+ try:
+ if isinstance(value, bytes) and len(value) == 16:
+ return uuid.UUID(bytes=value)
+ return uuid.UUID(value)
+ except (ValueError, AttributeError, TypeError) as error:
+ raise self.make_error("invalid_uuid") from error
+
+ def _deserialize(self, value, attr, data, **kwargs) -> uuid.UUID | None:
+ return self._validated(value)
+
+
+_NumType = typing.TypeVar("_NumType")
+
+
+class Number(Field, typing.Generic[_NumType]):
+ """Base class for number fields.
+
+ :param as_string: If `True`, format the serialized value as a string.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. versionchanged:: 3.24.0
+ `Number <marshmallow.fields.Number>` should no longer be used as a field within a `Schema <marshmallow.Schema>`.
+ Use `Integer <marshmallow.fields.Integer>`, `Float <marshmallow.fields.Float>`, or `Decimal <marshmallow.fields.Decimal>` instead.
+ """
+
+ num_type: type = float
+
+ #: Default error messages.
+ default_error_messages = {
+ "invalid": "Not a valid number.",
+ "too_large": "Number too large.",
+ }
+
+ def __init__(self, *, as_string: bool = False, **kwargs):
+ if self.__class__ is Number:
+ warnings.warn(
+ "`Number` field should not be instantiated. Use `Integer`, `Float`, or `Decimal` instead.",
+ ChangedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ self.as_string = as_string
+ super().__init__(**kwargs)
+
+ def _format_num(self, value) -> _NumType:
+ """Return the number value for value, given this field's `num_type`."""
+ return self.num_type(value)
+
+ def _validated(self, value: typing.Any) -> _NumType:
+ """Format the value or raise a :exc:`ValidationError` if an error occurs."""
+ # (value is True or value is False) is ~5x faster than isinstance(value, bool)
+ if value is True or value is False:
+ raise self.make_error("invalid", input=value)
+ try:
+ return self._format_num(value)
+ except (TypeError, ValueError) as error:
+ raise self.make_error("invalid", input=value) from error
+ except OverflowError as error:
+ raise self.make_error("too_large", input=value) from error
+
+ def _to_string(self, value: _NumType) -> str:
+ return str(value)
+
+ def _serialize(self, value, attr, obj, **kwargs) -> str | _NumType | None:
+ """Return a string if `self.as_string=True`, otherwise return this field's `num_type`."""
+ if value is None:
+ return None
+ ret: _NumType = self._format_num(value)
+ return self._to_string(ret) if self.as_string else ret
+
+ def _deserialize(self, value, attr, data, **kwargs) -> _NumType | None:
+ return self._validated(value)
+
+
+class Integer(Number[int]):
+ """An integer field.
+
+ :param strict: If `True`, only integer types are valid.
+ Otherwise, any value castable to `int` is valid.
+ :param kwargs: The same keyword arguments that :class:`Number` receives.
+ """
+
+ num_type = int
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid integer."}
+
+ def __init__(self, *, strict: bool = False, **kwargs):
+ self.strict = strict
+ super().__init__(**kwargs)
+
+ # override Number
+ def _validated(self, value: typing.Any) -> int:
+ if self.strict and not isinstance(value, numbers.Integral):
+ raise self.make_error("invalid", input=value)
+ return super()._validated(value)
+
+
+class Float(Number[float]):
+ """A double as an IEEE-754 double precision string.
+
+ :param allow_nan: If `True`, `NaN`, `Infinity` and `-Infinity` are allowed,
+ even though they are illegal according to the JSON specification.
+ :param as_string: If `True`, format the value as a string.
+ :param kwargs: The same keyword arguments that :class:`Number` receives.
+ """
+
+ num_type = float
+
+ #: Default error messages.
+ default_error_messages = {
+ "special": "Special numeric values (nan or infinity) are not permitted."
+ }
+
+ def __init__(self, *, allow_nan: bool = False, as_string: bool = False, **kwargs):
+ self.allow_nan = allow_nan
+ super().__init__(as_string=as_string, **kwargs)
+
+ def _validated(self, value: typing.Any) -> float:
+ num = super()._validated(value)
+ if self.allow_nan is False:
+ if math.isnan(num) or num == float("inf") or num == float("-inf"):
+ raise self.make_error("special")
+ return num
+
+
+class Decimal(Number[decimal.Decimal]):
+ """A field that (de)serializes to the Python ``decimal.Decimal`` type.
+ It's safe to use when dealing with money values, percentages, ratios
+ or other numbers where precision is critical.
+
+ .. warning::
+
+ This field serializes to a `decimal.Decimal` object by default. If you need
+ to render your data as JSON, keep in mind that the `json` module from the
+ standard library does not encode `decimal.Decimal`. Therefore, you must use
+ a JSON library that can handle decimals, such as `simplejson`, or serialize
+ to a string by passing ``as_string=True``.
+
+ .. warning::
+
+ If a JSON `float` value is passed to this field for deserialization it will
+ first be cast to its corresponding `string` value before being deserialized
+ to a `decimal.Decimal` object. The default `__str__` implementation of the
+ built-in Python `float` type may apply a destructive transformation upon
+ its input data and therefore cannot be relied upon to preserve precision.
+ To avoid this, you can instead pass a JSON `string` to be deserialized
+ directly.
+
+ :param places: How many decimal places to quantize the value. If `None`, does
+ not quantize the value.
+ :param rounding: How to round the value during quantize, for example
+ `decimal.ROUND_UP`. If `None`, uses the rounding value from
+ the current thread's context.
+ :param allow_nan: If `True`, `NaN`, `Infinity` and `-Infinity` are allowed,
+ even though they are illegal according to the JSON specification.
+ :param as_string: If `True`, serialize to a string instead of a Python
+ `decimal.Decimal` type.
+ :param kwargs: The same keyword arguments that :class:`Number` receives.
+
+ .. versionadded:: 1.2.0
+ """
+
+ num_type = decimal.Decimal
+
+ #: Default error messages.
+ default_error_messages = {
+ "special": "Special numeric values (nan or infinity) are not permitted."
+ }
+
+ def __init__(
+ self,
+ places: int | None = None,
+ rounding: str | None = None,
+ *,
+ allow_nan: bool = False,
+ as_string: bool = False,
+ **kwargs,
+ ):
+ self.places = (
+ decimal.Decimal((0, (1,), -places)) if places is not None else None
+ )
+ self.rounding = rounding
+ self.allow_nan = allow_nan
+ super().__init__(as_string=as_string, **kwargs)
+
+ # override Number
+ def _format_num(self, value):
+ num = decimal.Decimal(str(value))
+ if self.allow_nan:
+ if num.is_nan():
+ return decimal.Decimal("NaN") # avoid sNaN, -sNaN and -NaN
+ if self.places is not None and num.is_finite():
+ num = num.quantize(self.places, rounding=self.rounding)
+ return num
+
+ # override Number
+ def _validated(self, value: typing.Any) -> decimal.Decimal:
+ try:
+ num = super()._validated(value)
+ except decimal.InvalidOperation as error:
+ raise self.make_error("invalid") from error
+ if not self.allow_nan and (num.is_nan() or num.is_infinite()):
+ raise self.make_error("special")
+ return num
+
+ # override Number
+ def _to_string(self, value: decimal.Decimal) -> str:
+ return format(value, "f")
+
+
+class Boolean(Field):
+ """A boolean field.
+
+ :param truthy: Values that will (de)serialize to `True`. If an empty
+ set, any non-falsy value will deserialize to `True`. If `None`,
+ `marshmallow.fields.Boolean.truthy` will be used.
+ :param falsy: Values that will (de)serialize to `False`. If `None`,
+ `marshmallow.fields.Boolean.falsy` will be used.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+ """
+
+ #: Default truthy values.
+ truthy = {
+ "t",
+ "T",
+ "true",
+ "True",
+ "TRUE",
+ "on",
+ "On",
+ "ON",
+ "y",
+ "Y",
+ "yes",
+ "Yes",
+ "YES",
+ "1",
+ 1,
+ # Equal to 1
+ # True,
+ }
+ #: Default falsy values.
+ falsy = {
+ "f",
+ "F",
+ "false",
+ "False",
+ "FALSE",
+ "off",
+ "Off",
+ "OFF",
+ "n",
+ "N",
+ "no",
+ "No",
+ "NO",
+ "0",
+ 0,
+ # Equal to 0
+ # 0.0,
+ # False,
+ }
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid boolean."}
+
+ def __init__(
+ self,
+ *,
+ truthy: typing.Iterable | None = None,
+ falsy: typing.Iterable | None = None,
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+
+ if truthy is not None:
+ self.truthy = set(truthy)
+ if falsy is not None:
+ self.falsy = set(falsy)
+
+ def _serialize(
+ self, value: typing.Any, attr: str | None, obj: typing.Any, **kwargs
+ ):
+ if value is None:
+ return None
+
+ try:
+ if value in self.truthy:
+ return True
+ if value in self.falsy:
+ return False
+ except TypeError:
+ pass
+
+ return bool(value)
+
+ def _deserialize(self, value, attr, data, **kwargs):
+ if not self.truthy:
+ return bool(value)
+ try:
+ if value in self.truthy:
+ return True
+ if value in self.falsy:
+ return False
+ except TypeError as error:
+ raise self.make_error("invalid", input=value) from error
+ raise self.make_error("invalid", input=value)
+
+
+class DateTime(Field):
+ """A formatted datetime string.
+
+ Example: ``'2014-12-22T03:12:58.019077+00:00'``
+
+ :param format: Either ``"rfc"`` (for RFC822), ``"iso"`` (for ISO8601),
+ ``"timestamp"``, ``"timestamp_ms"`` (for a POSIX timestamp) or a date format string.
+ If `None`, defaults to "iso".
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. versionchanged:: 3.0.0rc9
+ Does not modify timezone information on (de)serialization.
+ .. versionchanged:: 3.19
+ Add timestamp as a format.
+ """
+
+ SERIALIZATION_FUNCS: dict[str, typing.Callable[[typing.Any], str | float]] = {
+ "iso": utils.isoformat,
+ "iso8601": utils.isoformat,
+ "rfc": utils.rfcformat,
+ "rfc822": utils.rfcformat,
+ "timestamp": utils.timestamp,
+ "timestamp_ms": utils.timestamp_ms,
+ }
+
+ DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], typing.Any]] = {
+ "iso": utils.from_iso_datetime,
+ "iso8601": utils.from_iso_datetime,
+ "rfc": utils.from_rfc,
+ "rfc822": utils.from_rfc,
+ "timestamp": utils.from_timestamp,
+ "timestamp_ms": utils.from_timestamp_ms,
+ }
+
+ DEFAULT_FORMAT = "iso"
+
+ OBJ_TYPE = "datetime"
+
+ SCHEMA_OPTS_VAR_NAME = "datetimeformat"
+
+ #: Default error messages.
+ default_error_messages = {
+ "invalid": "Not a valid {obj_type}.",
+ "invalid_awareness": "Not a valid {awareness} {obj_type}.",
+ "format": '"{input}" cannot be formatted as a {obj_type}.',
+ }
+
+ def __init__(self, format: str | None = None, **kwargs) -> None: # noqa: A002
+ super().__init__(**kwargs)
+ # Allow this to be None. It may be set later in the ``_serialize``
+ # or ``_deserialize`` methods. This allows a Schema to dynamically set the
+ # format, e.g. from a Meta option
+ self.format = format
+
+ def _bind_to_schema(self, field_name, schema):
+ super()._bind_to_schema(field_name, schema)
+ self.format = (
+ self.format
+ or getattr(self.root.opts, self.SCHEMA_OPTS_VAR_NAME)
+ or self.DEFAULT_FORMAT
+ )
+
+ def _serialize(self, value, attr, obj, **kwargs) -> str | float | None:
+ if value is None:
+ return None
+ data_format = self.format or self.DEFAULT_FORMAT
+ format_func = self.SERIALIZATION_FUNCS.get(data_format)
+ if format_func:
+ return format_func(value)
+ return value.strftime(data_format)
+
+ def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
+ data_format = self.format or self.DEFAULT_FORMAT
+ func = self.DESERIALIZATION_FUNCS.get(data_format)
+ try:
+ if func:
+ return func(value)
+ return self._make_object_from_format(value, data_format)
+ except (TypeError, AttributeError, ValueError) as error:
+ raise self.make_error(
+ "invalid", input=value, obj_type=self.OBJ_TYPE
+ ) from error
+
+ @staticmethod
+ def _make_object_from_format(value, data_format) -> dt.datetime:
+ return dt.datetime.strptime(value, data_format)
+
+
+class NaiveDateTime(DateTime):
+ """A formatted naive datetime string.
+
+ :param format: See :class:`DateTime`.
+ :param timezone: Used on deserialization. If `None`,
+ aware datetimes are rejected. If not `None`, aware datetimes are
+ converted to this timezone before their timezone information is
+ removed.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. versionadded:: 3.0.0rc9
+ """
+
+ AWARENESS = "naive"
+
+ def __init__(
+ self,
+ format: str | None = None, # noqa: A002
+ *,
+ timezone: dt.timezone | None = None,
+ **kwargs,
+ ) -> None:
+ super().__init__(format=format, **kwargs)
+ self.timezone = timezone
+
+ def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
+ ret = super()._deserialize(value, attr, data, **kwargs)
+ if is_aware(ret):
+ if self.timezone is None:
+ raise self.make_error(
+ "invalid_awareness",
+ awareness=self.AWARENESS,
+ obj_type=self.OBJ_TYPE,
+ )
+ ret = ret.astimezone(self.timezone).replace(tzinfo=None)
+ return ret
+
+
+class AwareDateTime(DateTime):
+ """A formatted aware datetime string.
+
+ :param format: See :class:`DateTime`.
+ :param default_timezone: Used on deserialization. If `None`, naive
+ datetimes are rejected. If not `None`, naive datetimes are set this
+ timezone.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. versionadded:: 3.0.0rc9
+ """
+
+ AWARENESS = "aware"
+
+ def __init__(
+ self,
+ format: str | None = None, # noqa: A002
+ *,
+ default_timezone: dt.tzinfo | None = None,
+ **kwargs,
+ ) -> None:
+ super().__init__(format=format, **kwargs)
+ self.default_timezone = default_timezone
+
+ def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
+ ret = super()._deserialize(value, attr, data, **kwargs)
+ if not is_aware(ret):
+ if self.default_timezone is None:
+ raise self.make_error(
+ "invalid_awareness",
+ awareness=self.AWARENESS,
+ obj_type=self.OBJ_TYPE,
+ )
+ ret = ret.replace(tzinfo=self.default_timezone)
+ return ret
+
+
+class Time(DateTime):
+ """A formatted time string.
+
+ Example: ``'03:12:58.019077'``
+
+ :param format: Either ``"iso"`` (for ISO8601) or a date format string.
+ If `None`, defaults to "iso".
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+ """
+
+ SERIALIZATION_FUNCS = {"iso": utils.to_iso_time, "iso8601": utils.to_iso_time}
+
+ DESERIALIZATION_FUNCS = {"iso": utils.from_iso_time, "iso8601": utils.from_iso_time}
+
+ DEFAULT_FORMAT = "iso"
+
+ OBJ_TYPE = "time"
+
+ SCHEMA_OPTS_VAR_NAME = "timeformat"
+
+ @staticmethod
+ def _make_object_from_format(value, data_format):
+ return dt.datetime.strptime(value, data_format).time()
+
+
+class Date(DateTime):
+ """ISO8601-formatted date string.
+
+ :param format: Either ``"iso"`` (for ISO8601) or a date format string.
+ If `None`, defaults to "iso".
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+ """
+
+ #: Default error messages.
+ default_error_messages = {
+ "invalid": "Not a valid date.",
+ "format": '"{input}" cannot be formatted as a date.',
+ }
+
+ SERIALIZATION_FUNCS = {"iso": utils.to_iso_date, "iso8601": utils.to_iso_date}
+
+ DESERIALIZATION_FUNCS = {"iso": utils.from_iso_date, "iso8601": utils.from_iso_date}
+
+ DEFAULT_FORMAT = "iso"
+
+ OBJ_TYPE = "date"
+
+ SCHEMA_OPTS_VAR_NAME = "dateformat"
+
+ @staticmethod
+ def _make_object_from_format(value, data_format):
+ return dt.datetime.strptime(value, data_format).date()
+
+
+class TimeDelta(Field):
+ """A field that (de)serializes a :class:`datetime.timedelta` object to an
+ integer or float and vice versa. The integer or float can represent the
+ number of days, seconds or microseconds.
+
+ :param precision: Influences how the integer or float is interpreted during
+ (de)serialization. Must be 'days', 'seconds', 'microseconds',
+ 'milliseconds', 'minutes', 'hours' or 'weeks'.
+ :param serialization_type: Whether to (de)serialize to a `int` or `float`.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ Integer Caveats
+ ---------------
+ Any fractional parts (which depends on the precision used) will be truncated
+ when serializing using `int`.
+
+ Float Caveats
+ -------------
+ Use of `float` when (de)serializing may result in data precision loss due
+ to the way machines handle floating point values.
+
+ Regardless of the precision chosen, the fractional part when using `float`
+ will always be truncated to microseconds.
+ For example, `1.12345` interpreted as microseconds will result in `timedelta(microseconds=1)`.
+
+ .. versionchanged:: 3.17.0
+ Allow (de)serialization to `float` through use of a new `serialization_type` parameter.
+ `int` is the default to retain previous behaviour.
+ """
+
+ DAYS = "days"
+ SECONDS = "seconds"
+ MICROSECONDS = "microseconds"
+ MILLISECONDS = "milliseconds"
+ MINUTES = "minutes"
+ HOURS = "hours"
+ WEEKS = "weeks"
+
+ #: Default error messages.
+ default_error_messages = {
+ "invalid": "Not a valid period of time.",
+ "format": "{input!r} cannot be formatted as a timedelta.",
+ }
+
+ def __init__(
+ self,
+ precision: str = SECONDS,
+ serialization_type: type[int | float] = int,
+ **kwargs,
+ ):
+ precision = precision.lower()
+ units = (
+ self.DAYS,
+ self.SECONDS,
+ self.MICROSECONDS,
+ self.MILLISECONDS,
+ self.MINUTES,
+ self.HOURS,
+ self.WEEKS,
+ )
+
+ if precision not in units:
+ msg = 'The precision must be {} or "{}".'.format(
+ ", ".join([f'"{each}"' for each in units[:-1]]), units[-1]
+ )
+ raise ValueError(msg)
+
+ if serialization_type not in (int, float):
+ raise ValueError("The serialization type must be one of int or float")
+
+ self.precision = precision
+ self.serialization_type = serialization_type
+ super().__init__(**kwargs)
+
+ def _serialize(self, value, attr, obj, **kwargs):
+ if value is None:
+ return None
+
+ base_unit = dt.timedelta(**{self.precision: 1})
+
+ if self.serialization_type is int:
+ delta = utils.timedelta_to_microseconds(value)
+ unit = utils.timedelta_to_microseconds(base_unit)
+ return delta // unit
+ assert self.serialization_type is float # noqa: S101
+ return value.total_seconds() / base_unit.total_seconds()
+
+ def _deserialize(self, value, attr, data, **kwargs):
+ try:
+ value = self.serialization_type(value)
+ except (TypeError, ValueError) as error:
+ raise self.make_error("invalid") from error
+
+ kwargs = {self.precision: value}
+
+ try:
+ return dt.timedelta(**kwargs)
+ except OverflowError as error:
+ raise self.make_error("invalid") from error
+
+
+class Mapping(Field):
+ """An abstract class for objects with key-value pairs. This class should not be used within schemas.
+
+ :param keys: A field class or instance for dict keys.
+ :param values: A field class or instance for dict values.
+ :param kwargs: The same keyword arguments that :class:`Field` receives.
+
+ .. note::
+ When the structure of nested data is not known, you may omit the
+ `keys` and `values` arguments to prevent content validation.
+
+ .. versionadded:: 3.0.0rc4
+ .. versionchanged:: 3.24.0
+ `Mapping <marshmallow.fields.Mapping>` should no longer be used as a field within a `Schema <marshmallow.Schema>`.
+ Use `Dict <marshmallow.fields.Dict>` instead.
+ """
+
+ mapping_type = dict
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid mapping type."}
+
+ def __init__(
+ self,
+ keys: Field | type[Field] | None = None,
+ values: Field | type[Field] | None = None,
+ **kwargs,
+ ):
+ if self.__class__ is Mapping:
+ warnings.warn(
+ "`Mapping` field should not be instantiated. Use `Dict` instead.",
+ ChangedInMarshmallow4Warning,
+ stacklevel=2,
+ )
+ super().__init__(**kwargs)
+ if keys is None:
+ self.key_field = None
+ else:
+ try:
+ self.key_field = resolve_field_instance(keys)
+ except FieldInstanceResolutionError as error:
+ raise ValueError(
+ '"keys" must be a subclass or instance of '
+ "marshmallow.base.FieldABC."
+ ) from error
+
+ if values is None:
+ self.value_field = None
+ else:
+ try:
+ self.value_field = resolve_field_instance(values)
+ except FieldInstanceResolutionError as error:
+ raise ValueError(
+ '"values" must be a subclass or instance of '
+ "marshmallow.base.FieldABC."
+ ) from error
+ if isinstance(self.value_field, Nested):
+ self.only = self.value_field.only
+ self.exclude = self.value_field.exclude
+
+ def _bind_to_schema(self, field_name, schema):
+ super()._bind_to_schema(field_name, schema)
+ if self.value_field:
+ self.value_field = copy.deepcopy(self.value_field)
+ self.value_field._bind_to_schema(field_name, self)
+ if isinstance(self.value_field, Nested):
+ self.value_field.only = self.only
+ self.value_field.exclude = self.exclude
+ if self.key_field:
+ self.key_field = copy.deepcopy(self.key_field)
+ self.key_field._bind_to_schema(field_name, self)
+
+ def _serialize(self, value, attr, obj, **kwargs):
+ if value is None:
+ return None
+ if not self.value_field and not self.key_field:
+ return self.mapping_type(value)
+
+ # Serialize keys
+ if self.key_field is None:
+ keys = {k: k for k in value}
+ else:
+ keys = {
+ k: self.key_field._serialize(k, None, None, **kwargs) for k in value
+ }
+
+ # Serialize values
+ result = self.mapping_type()
+ if self.value_field is None:
+ for k, v in value.items():
+ if k in keys:
+ result[keys[k]] = v
+ else:
+ for k, v in value.items():
+ result[keys[k]] = self.value_field._serialize(v, None, None, **kwargs)
+
+ return result
+
+ def _deserialize(self, value, attr, data, **kwargs):
+ if not isinstance(value, _Mapping):
+ raise self.make_error("invalid")
+ if not self.value_field and not self.key_field:
+ return self.mapping_type(value)
+
+ errors = collections.defaultdict(dict)
+
+ # Deserialize keys
+ if self.key_field is None:
+ keys = {k: k for k in value}
+ else:
+ keys = {}
+ for key in value:
+ try:
+ keys[key] = self.key_field.deserialize(key, **kwargs)
+ except ValidationError as error:
+ errors[key]["key"] = error.messages
+
+ # Deserialize values
+ result = self.mapping_type()
+ if self.value_field is None:
+ for k, v in value.items():
+ if k in keys:
+ result[keys[k]] = v
+ else:
+ for key, val in value.items():
+ try:
+ deser_val = self.value_field.deserialize(val, **kwargs)
+ except ValidationError as error:
+ errors[key]["value"] = error.messages
+ if error.valid_data is not None and key in keys:
+ result[keys[key]] = error.valid_data
+ else:
+ if key in keys:
+ result[keys[key]] = deser_val
+
+ if errors:
+ raise ValidationError(errors, valid_data=result)
+
+ return result
+
+
+class Dict(Mapping):
+ """A dict field. Supports dicts and dict-like objects. Extends
+ Mapping with dict as the mapping_type.
+
+ Example: ::
+
+ numbers = fields.Dict(keys=fields.Str(), values=fields.Float())
+
+ :param kwargs: The same keyword arguments that :class:`Mapping` receives.
+
+ .. versionadded:: 2.1.0
+ """
+
+ mapping_type = dict
+
+
+class Url(String):
+ """An URL field.
+
+ :param default: Default value for the field if the attribute is not set.
+ :param relative: Whether to allow relative URLs.
+ :param absolute: Whether to allow absolute URLs.
+ :param require_tld: Whether to reject non-FQDN hostnames.
+ :param schemes: Valid schemes. By default, ``http``, ``https``,
+ ``ftp``, and ``ftps`` are allowed.
+ :param kwargs: The same keyword arguments that :class:`String` receives.
+ """
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid URL."}
+
+ def __init__(
+ self,
+ *,
+ relative: bool = False,
+ absolute: bool = True,
+ schemes: types.StrSequenceOrSet | None = None,
+ require_tld: bool = True,
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+
+ self.relative = relative
+ self.absolute = absolute
+ self.require_tld = require_tld
+ # Insert validation into self.validators so that multiple errors can be stored.
+ validator = validate.URL(
+ relative=self.relative,
+ absolute=self.absolute,
+ schemes=schemes,
+ require_tld=self.require_tld,
+ error=self.error_messages["invalid"],
+ )
+ self.validators.insert(0, validator)
+
+
+class Email(String):
+ """An email field.
+
+ :param args: The same positional arguments that :class:`String` receives.
+ :param kwargs: The same keyword arguments that :class:`String` receives.
+ """
+
+ #: Default error messages.
+ default_error_messages = {"invalid": "Not a valid email address."}
+
+ def __init__(self, *args, **kwargs) -> None:
+ super().__init__(*args, **kwargs)
+ # Insert validation into self.validators so that multiple errors can be stored.
+ validator = validate.Email(error=self.error_messages["invalid"])
+ self.validators.insert(0, validator)
+
+
+class IP(Field):
+ """A IP address field.
+
+ :param exploded: If `True`, serialize ipv6 address in long form, ie. with groups
+ consisting entirely of zeros included.
+
+ .. versionadded:: 3.8.0
+ """
+
+ default_error_messages = {"invalid_ip": "Not a valid IP address."}
+
+ DESERIALIZATION_CLASS: type | None = None
+
+ def __init__(self, *args, exploded=False, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.exploded = exploded
+
+ def _serialize(self, value, attr, obj, **kwargs) -> str | None:
+ if value is None:
+ return None
+ if self.exploded:
+ return value.exploded
+ return value.compressed
+
+ def _deserialize(
+ self, value, attr, data, **kwargs
+ ) -> ipaddress.IPv4Address | ipaddress.IPv6Address | None:
+ if value is None:
+ return None
+ try:
+ return (self.DESERIALIZATION_CLASS or ipaddress.ip_address)(
+ utils.ensure_text_type(value)
+ )
+ except (ValueError, TypeError) as error:
+ raise self.make_error("invalid_ip") from error
+
+
+class IPv4(IP):
+ """A IPv4 address field.
+
+ .. versionadded:: 3.8.0
+ """
+
+ default_error_messages = {"invalid_ip": "Not a valid IPv4 address."}
+
+ DESERIALIZATION_CLASS = ipaddress.IPv4Address
+
+
+class IPv6(IP):
+ """A IPv6 address field.
+
+ .. versionadded:: 3.8.0
+ """
+
+ default_error_messages = {"invalid_ip": "Not a valid IPv6 address."}
+
+ DESERIALIZATION_CLASS = ipaddress.IPv6Address
+
+
+class IPInterface(Field):
+ """A IPInterface field.
+
+ IP interface is the non-strict form of the IPNetwork type where arbitrary host
+ addresses are always accepted.
+
+ IPAddress and mask e.g. '192.168.0.2/24' or '192.168.0.2/255.255.255.0'
+
+ see https://python.readthedocs.io/en/latest/library/ipaddress.html#interface-objects
+
+ :param exploded: If `True`, serialize ipv6 interface in long form, ie. with groups
+ consisting entirely of zeros included.
+ """
+
+ default_error_messages = {"invalid_ip_interface": "Not a valid IP interface."}
+
+ DESERIALIZATION_CLASS: type | None = None
+
+ def __init__(self, *args, exploded: bool = False, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.exploded = exploded
+
+ def _serialize(self, value, attr, obj, **kwargs) -> str | None:
+ if value is None:
+ return None
+ if self.exploded:
+ return value.exploded
+ return value.compressed
+
+ def _deserialize(self, value, attr, data, **kwargs) -> None | (
+ ipaddress.IPv4Interface | ipaddress.IPv6Interface
+ ):
+ if value is None:
+ return None
+ try:
+ return (self.DESERIALIZATION_CLASS or ipaddress.ip_interface)(
+ utils.ensure_text_type(value)
+ )
+ except (ValueError, TypeError) as error:
+ raise self.make_error("invalid_ip_interface") from error
+
+
+class IPv4Interface(IPInterface):
+ """A IPv4 Network Interface field."""
+
+ default_error_messages = {"invalid_ip_interface": "Not a valid IPv4 interface."}
+
+ DESERIALIZATION_CLASS = ipaddress.IPv4Interface
+
+
+class IPv6Interface(IPInterface):
+ """A IPv6 Network Interface field."""
+
+ default_error_messages = {"invalid_ip_interface": "Not a valid IPv6 interface."}
+
+ DESERIALIZATION_CLASS = ipaddress.IPv6Interface
+
+
+class Enum(Field):
+ """An Enum field (de)serializing enum members by symbol (name) or by value.
+
+ :param enum: Enum class
+ :param by_value: Whether to (de)serialize by value or by name,
+ or Field class or instance to use to (de)serialize by value. Defaults to False.
+
+ If `by_value` is `False` (default), enum members are (de)serialized by symbol (name).
+ If it is `True`, they are (de)serialized by value using :class:`Raw`.
+ If it is a field instance or class, they are (de)serialized by value using this field.
+
+ .. versionadded:: 3.18.0
+ """
+
+ default_error_messages = {
+ "unknown": "Must be one of: {choices}.",
+ }
+
+ def __init__(
+ self,
+ enum: type[EnumType],
+ *,
+ by_value: bool | Field | type[Field] = False,
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+ self.enum = enum
+ self.by_value = by_value
+
+ # Serialization by name
+ if by_value is False:
+ self.field: Field = String()
+ self.choices_text = ", ".join(
+ str(self.field._serialize(m, None, None)) for m in enum.__members__
+ )
+ # Serialization by value
+ else:
+ if by_value is True:
+ self.field = Raw()
+ else:
+ try:
+ self.field = resolve_field_instance(by_value)
+ except FieldInstanceResolutionError as error:
+ raise ValueError(
+ '"by_value" must be either a bool or a subclass or instance of '
+ "marshmallow.base.FieldABC."
+ ) from error
+ self.choices_text = ", ".join(
+ str(self.field._serialize(m.value, None, None)) for m in enum
+ )
+
+ def _serialize(self, value, attr, obj, **kwargs):
+ if value is None:
+ return None
+ if self.by_value:
+ val = value.value
+ else:
+ val = value.name
+ return self.field._serialize(val, attr, obj, **kwargs)
+
+ def _deserialize(self, value, attr, data, **kwargs):
+ val = self.field._deserialize(value, attr, data, **kwargs)
+ if self.by_value:
+ try:
+ return self.enum(val)
+ except ValueError as error:
+ raise self.make_error("unknown", choices=self.choices_text) from error
+ try:
+ return getattr(self.enum, val)
+ except AttributeError as error:
+ raise self.make_error("unknown", choices=self.choices_text) from error
+
+
+class Method(Field):
+ """A field that takes the value returned by a `Schema <marshmallow.Schema>` method.
+
+ :param serialize: The name of the Schema method from which
+ to retrieve the value. The method must take an argument ``obj``
+ (in addition to self) that is the object to be serialized.
+ :param deserialize: Optional name of the Schema method for deserializing
+ a value The method must take a single argument ``value``, which is the
+ value to deserialize.
+
+ .. versionchanged:: 2.3.0
+ Deprecated ``method_name`` parameter in favor of ``serialize`` and allow
+ ``serialize`` to not be passed at all.
+
+ .. versionchanged:: 3.0.0
+ Removed ``method_name`` parameter.
+ """
+
+ _CHECK_ATTRIBUTE = False
+
+ def __init__(
+ self,
+ serialize: str | None = None,
+ deserialize: str | None = None,
+ **kwargs,
+ ):
+ # Set dump_only and load_only based on arguments
+ kwargs["dump_only"] = bool(serialize) and not bool(deserialize)
+ kwargs["load_only"] = bool(deserialize) and not bool(serialize)
+ super().__init__(**kwargs)
+ self.serialize_method_name = serialize
+ self.deserialize_method_name = deserialize
+ self._serialize_method = None
+ self._deserialize_method = None
+
+ def _bind_to_schema(self, field_name, schema):
+ if self.serialize_method_name:
+ self._serialize_method = utils.callable_or_raise(
+ getattr(schema, self.serialize_method_name)
+ )
+
+ if self.deserialize_method_name:
+ self._deserialize_method = utils.callable_or_raise(
+ getattr(schema, self.deserialize_method_name)
+ )
+
+ super()._bind_to_schema(field_name, schema)
+
+ def _serialize(self, value, attr, obj, **kwargs):
+ if self._serialize_method is not None:
+ return self._serialize_method(obj)
+ return missing_
+
+ def _deserialize(self, value, attr, data, **kwargs):
+ if self._deserialize_method is not None:
+ return self._deserialize_method(value)
+ return value
+
+
+class Function(Field):
+ """A field that takes the value returned by a function.
+
+ :param serialize: A callable from which to retrieve the value.
+ The function must take a single argument ``obj`` which is the object
+ to be serialized. It can also optionally take a ``context`` argument,
+ which is a dictionary of context variables passed to the serializer.
+ If no callable is provided then the ```load_only``` flag will be set
+ to True.
+ :param deserialize: A callable from which to retrieve the value.
+ The function must take a single argument ``value`` which is the value
+ to be deserialized. It can also optionally take a ``context`` argument,
+ which is a dictionary of context variables passed to the deserializer.
+ If no callable is provided then ```value``` will be passed through
+ unchanged.
+
+ .. versionchanged:: 2.3.0
+ Deprecated ``func`` parameter in favor of ``serialize``.
+
+ .. versionchanged:: 3.0.0a1
+ Removed ``func`` parameter.
+ """
+
+ _CHECK_ATTRIBUTE = False
+
+ def __init__(
+ self,
+ serialize: (
+ typing.Callable[[typing.Any], typing.Any]
+ | typing.Callable[[typing.Any, dict], typing.Any]
+ | None
+ ) = None,
+ deserialize: (
+ typing.Callable[[typing.Any], typing.Any]
+ | typing.Callable[[typing.Any, dict], typing.Any]
+ | None
+ ) = None,
+ **kwargs,
+ ):
+ # Set dump_only and load_only based on arguments
+ kwargs["dump_only"] = bool(serialize) and not bool(deserialize)
+ kwargs["load_only"] = bool(deserialize) and not bool(serialize)
+ super().__init__(**kwargs)
+ self.serialize_func = serialize and utils.callable_or_raise(serialize)
+ self.deserialize_func = deserialize and utils.callable_or_raise(deserialize)
+
+ def _serialize(self, value, attr, obj, **kwargs):
+ return self._call_or_raise(self.serialize_func, obj, attr)
+
+ def _deserialize(self, value, attr, data, **kwargs):
+ if self.deserialize_func:
+ return self._call_or_raise(self.deserialize_func, value, attr)
+ return value
+
+ def _call_or_raise(self, func, value, attr):
+ if len(utils.get_func_args(func)) > 1:
+ if self.parent.context is None:
+ msg = f"No context available for Function field {attr!r}"
+ raise ValidationError(msg)
+ return func(value, self.parent.context)
+ return func(value)
+
+
+class Constant(Field):
+ """A field that (de)serializes to a preset constant. If you only want the
+ constant added for serialization or deserialization, you should use
+ ``dump_only=True`` or ``load_only=True`` respectively.
+
+ :param constant: The constant to return for the field attribute.
+ """
+
+ _CHECK_ATTRIBUTE = False
+
+ def __init__(self, constant: typing.Any, **kwargs):
+ super().__init__(**kwargs)
+ self.constant = constant
+ self.load_default = constant
+ self.dump_default = constant
+
+ def _serialize(self, value, *args, **kwargs):
+ return self.constant
+
+ def _deserialize(self, value, *args, **kwargs):
+ return self.constant
+
+
+class Inferred(Field):
+ """A field that infers how to serialize, based on the value type.
+
+ .. warning::
+
+ This class is treated as private API.
+ Users should not need to use this class directly.
+ """
+
+ def __init__(self):
+ super().__init__()
+ # We memoize the fields to avoid creating and binding new fields
+ # every time on serialization.
+ self._field_cache = {}
+
+ def _serialize(self, value, attr, obj, **kwargs):
+ field_cls = self.root.TYPE_MAPPING.get(type(value))
+ if field_cls is None:
+ field = super()
+ else:
+ field = self._field_cache.get(field_cls)
+ if field is None:
+ field = field_cls()
+ field._bind_to_schema(self.name, self.parent)
+ self._field_cache[field_cls] = field
+ return field._serialize(value, attr, obj, **kwargs)
+
+
+# Aliases
+URL = Url
+Str = String
+Bool = Boolean
+Int = Integer