about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/marshmallow/decorators.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/marshmallow/decorators.py')
-rw-r--r--.venv/lib/python3.12/site-packages/marshmallow/decorators.py238
1 files changed, 238 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/marshmallow/decorators.py b/.venv/lib/python3.12/site-packages/marshmallow/decorators.py
new file mode 100644
index 00000000..eefba6c1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/marshmallow/decorators.py
@@ -0,0 +1,238 @@
+"""Decorators for registering schema pre-processing and post-processing methods.
+These should be imported from the top-level `marshmallow` module.
+
+Methods decorated with
+`pre_load <marshmallow.decorators.pre_load>`, `post_load <marshmallow.decorators.post_load>`,
+`pre_dump <marshmallow.decorators.pre_dump>`, `post_dump <marshmallow.decorators.post_dump>`,
+and `validates_schema <marshmallow.decorators.validates_schema>` receive
+``many`` as a keyword argument. In addition, `pre_load <marshmallow.decorators.pre_load>`,
+`post_load <marshmallow.decorators.post_load>`,
+and `validates_schema <marshmallow.decorators.validates_schema>` receive
+``partial``. If you don't need these arguments, add ``**kwargs`` to your method
+signature.
+
+
+Example: ::
+
+    from marshmallow import (
+        Schema,
+        pre_load,
+        pre_dump,
+        post_load,
+        validates_schema,
+        validates,
+        fields,
+        ValidationError,
+    )
+
+
+    class UserSchema(Schema):
+        email = fields.Str(required=True)
+        age = fields.Integer(required=True)
+
+        @post_load
+        def lowerstrip_email(self, item, many, **kwargs):
+            item["email"] = item["email"].lower().strip()
+            return item
+
+        @pre_load(pass_many=True)
+        def remove_envelope(self, data, many, **kwargs):
+            namespace = "results" if many else "result"
+            return data[namespace]
+
+        @post_dump(pass_many=True)
+        def add_envelope(self, data, many, **kwargs):
+            namespace = "results" if many else "result"
+            return {namespace: data}
+
+        @validates_schema
+        def validate_email(self, data, **kwargs):
+            if len(data["email"]) < 3:
+                raise ValidationError("Email must be more than 3 characters", "email")
+
+        @validates("age")
+        def validate_age(self, data, **kwargs):
+            if data < 14:
+                raise ValidationError("Too young!")
+
+.. note::
+    These decorators only work with instance methods. Class and static
+    methods are not supported.
+
+.. warning::
+    The invocation order of decorated methods of the same type is not guaranteed.
+    If you need to guarantee order of different processing steps, you should put
+    them in the same processing method.
+"""
+
+from __future__ import annotations
+
+import functools
+from collections import defaultdict
+from typing import Any, Callable, cast
+
+PRE_DUMP = "pre_dump"
+POST_DUMP = "post_dump"
+PRE_LOAD = "pre_load"
+POST_LOAD = "post_load"
+VALIDATES = "validates"
+VALIDATES_SCHEMA = "validates_schema"
+
+
+class MarshmallowHook:
+    __marshmallow_hook__: dict[str, list[tuple[bool, Any]]] | None = None
+
+
+def validates(field_name: str) -> Callable[..., Any]:
+    """Register a field validator.
+
+    :param field_name: Name of the field that the method validates.
+    """
+    return set_hook(None, VALIDATES, field_name=field_name)
+
+
+def validates_schema(
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+    pass_original: bool = False,  # noqa: FBT001, FBT002
+    skip_on_field_errors: bool = True,  # noqa: FBT001, FBT002
+) -> Callable[..., Any]:
+    """Register a schema-level validator.
+
+    By default it receives a single object at a time, transparently handling the ``many``
+    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.validate` call.
+    If ``pass_many=True``, the raw data (which may be a collection) is passed.
+
+    If ``pass_original=True``, the original data (before unmarshalling) will be passed as
+    an additional argument to the method.
+
+    If ``skip_on_field_errors=True``, this validation method will be skipped whenever
+    validation errors have been detected when validating fields.
+
+    .. versionchanged:: 3.0.0b1
+        ``skip_on_field_errors`` defaults to `True`.
+
+    .. versionchanged:: 3.0.0
+        ``partial`` and ``many`` are always passed as keyword arguments to
+        the decorated method.
+    """
+    return set_hook(
+        fn,
+        VALIDATES_SCHEMA,
+        many=pass_many,
+        pass_original=pass_original,
+        skip_on_field_errors=skip_on_field_errors,
+    )
+
+
+def pre_dump(
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+) -> Callable[..., Any]:
+    """Register a method to invoke before serializing an object. The method
+    receives the object to be serialized and returns the processed object.
+
+    By default it receives a single object at a time, transparently handling the ``many``
+    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.dump` call.
+    If ``pass_many=True``, the raw data (which may be a collection) is passed.
+
+    .. versionchanged:: 3.0.0
+        ``many`` is always passed as a keyword arguments to the decorated method.
+    """
+    return set_hook(fn, PRE_DUMP, many=pass_many)
+
+
+def post_dump(
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+    pass_original: bool = False,  # noqa: FBT001, FBT002
+) -> Callable[..., Any]:
+    """Register a method to invoke after serializing an object. The method
+    receives the serialized object and returns the processed object.
+
+    By default it receives a single object at a time, transparently handling the ``many``
+    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.dump` call.
+    If ``pass_many=True``, the raw data (which may be a collection) is passed.
+
+    If ``pass_original=True``, the original data (before serializing) will be passed as
+    an additional argument to the method.
+
+    .. versionchanged:: 3.0.0
+        ``many`` is always passed as a keyword arguments to the decorated method.
+    """
+    return set_hook(fn, POST_DUMP, many=pass_many, pass_original=pass_original)
+
+
+def pre_load(
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+) -> Callable[..., Any]:
+    """Register a method to invoke before deserializing an object. The method
+    receives the data to be deserialized and returns the processed data.
+
+    By default it receives a single object at a time, transparently handling the ``many``
+    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.load` call.
+    If ``pass_many=True``, the raw data (which may be a collection) is passed.
+
+    .. versionchanged:: 3.0.0
+        ``partial`` and ``many`` are always passed as keyword arguments to
+        the decorated method.
+    """
+    return set_hook(fn, PRE_LOAD, many=pass_many)
+
+
+def post_load(
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+    pass_original: bool = False,  # noqa: FBT001, FBT002
+) -> Callable[..., Any]:
+    """Register a method to invoke after deserializing an object. The method
+    receives the deserialized data and returns the processed data.
+
+    By default it receives a single object at a time, transparently handling the ``many``
+    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.load` call.
+    If ``pass_many=True``, the raw data (which may be a collection) is passed.
+
+    If ``pass_original=True``, the original data (before deserializing) will be passed as
+    an additional argument to the method.
+
+    .. versionchanged:: 3.0.0
+        ``partial`` and ``many`` are always passed as keyword arguments to
+        the decorated method.
+    """
+    return set_hook(fn, POST_LOAD, many=pass_many, pass_original=pass_original)
+
+
+def set_hook(
+    fn: Callable[..., Any] | None,
+    tag: str,
+    many: bool = False,  # noqa: FBT001, FBT002
+    **kwargs: Any,
+) -> Callable[..., Any]:
+    """Mark decorated function as a hook to be picked up later.
+    You should not need to use this method directly.
+
+    .. note::
+        Currently only works with functions and instance methods. Class and
+        static methods are not supported.
+
+    :return: Decorated function if supplied, else this decorator with its args
+        bound.
+    """
+    # Allow using this as either a decorator or a decorator factory.
+    if fn is None:
+        return functools.partial(set_hook, tag=tag, many=many, **kwargs)
+
+    # Set a __marshmallow_hook__ attribute instead of wrapping in some class,
+    # because I still want this to end up as a normal (unbound) method.
+    function = cast(MarshmallowHook, fn)
+    try:
+        hook_config = function.__marshmallow_hook__
+    except AttributeError:
+        function.__marshmallow_hook__ = hook_config = defaultdict(list)
+    # Also save the kwargs for the tagged function on
+    # __marshmallow_hook__, keyed by <tag>
+    if hook_config is not None:
+        hook_config[tag].append((many, kwargs))
+
+    return fn