Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
decorators.py239 linesDownload Raw Back to marshmallow
1"""Decorators for registering schema pre-processing and post-processing methods.2These should be imported from the top-level `marshmallow` module.3 4Methods decorated with5`pre_load <marshmallow.decorators.pre_load>`, `post_load <marshmallow.decorators.post_load>`,6`pre_dump <marshmallow.decorators.pre_dump>`, `post_dump <marshmallow.decorators.post_dump>`,7and `validates_schema <marshmallow.decorators.validates_schema>` receive8``many`` as a keyword argument. In addition, `pre_load <marshmallow.decorators.pre_load>`,9`post_load <marshmallow.decorators.post_load>`,10and `validates_schema <marshmallow.decorators.validates_schema>` receive11``partial``. If you don't need these arguments, add ``**kwargs`` to your method12signature.13 14 15Example: ::16 17    from marshmallow import (18        Schema,19        pre_load,20        pre_dump,21        post_load,22        validates_schema,23        validates,24        fields,25        ValidationError,26    )27 28 29    class UserSchema(Schema):30        email = fields.Str(required=True)31        age = fields.Integer(required=True)32 33        @post_load34        def lowerstrip_email(self, item, many, **kwargs):35            item["email"] = item["email"].lower().strip()36            return item37 38        @pre_load(pass_many=True)39        def remove_envelope(self, data, many, **kwargs):40            namespace = "results" if many else "result"41            return data[namespace]42 43        @post_dump(pass_many=True)44        def add_envelope(self, data, many, **kwargs):45            namespace = "results" if many else "result"46            return {namespace: data}47 48        @validates_schema49        def validate_email(self, data, **kwargs):50            if len(data["email"]) < 3:51                raise ValidationError("Email must be more than 3 characters", "email")52 53        @validates("age")54        def validate_age(self, data, **kwargs):55            if data < 14:56                raise ValidationError("Too young!")57 58.. note::59    These decorators only work with instance methods. Class and static60    methods are not supported.61 62.. warning::63    The invocation order of decorated methods of the same type is not guaranteed.64    If you need to guarantee order of different processing steps, you should put65    them in the same processing method.66"""67 68from __future__ import annotations69 70import functools71from collections import defaultdict72from typing import Any, Callable, cast73 74PRE_DUMP = "pre_dump"75POST_DUMP = "post_dump"76PRE_LOAD = "pre_load"77POST_LOAD = "post_load"78VALIDATES = "validates"79VALIDATES_SCHEMA = "validates_schema"80 81 82class MarshmallowHook:83    __marshmallow_hook__: dict[str, list[tuple[bool, Any]]] | None = None84 85 86def validates(field_name: str) -> Callable[..., Any]:87    """Register a field validator.88 89    :param field_name: Name of the field that the method validates.90    """91    return set_hook(None, VALIDATES, field_name=field_name)92 93 94def validates_schema(95    fn: Callable[..., Any] | None = None,96    pass_many: bool = False,  # noqa: FBT001, FBT00297    pass_original: bool = False,  # noqa: FBT001, FBT00298    skip_on_field_errors: bool = True,  # noqa: FBT001, FBT00299) -> Callable[..., Any]:100    """Register a schema-level validator.101 102    By default it receives a single object at a time, transparently handling the ``many``103    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.validate` call.104    If ``pass_many=True``, the raw data (which may be a collection) is passed.105 106    If ``pass_original=True``, the original data (before unmarshalling) will be passed as107    an additional argument to the method.108 109    If ``skip_on_field_errors=True``, this validation method will be skipped whenever110    validation errors have been detected when validating fields.111 112    .. versionchanged:: 3.0.0b1113        ``skip_on_field_errors`` defaults to `True`.114 115    .. versionchanged:: 3.0.0116        ``partial`` and ``many`` are always passed as keyword arguments to117        the decorated method.118    """119    return set_hook(120        fn,121        VALIDATES_SCHEMA,122        many=pass_many,123        pass_original=pass_original,124        skip_on_field_errors=skip_on_field_errors,125    )126 127 128def pre_dump(129    fn: Callable[..., Any] | None = None,130    pass_many: bool = False,  # noqa: FBT001, FBT002131) -> Callable[..., Any]:132    """Register a method to invoke before serializing an object. The method133    receives the object to be serialized and returns the processed object.134 135    By default it receives a single object at a time, transparently handling the ``many``136    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.dump` call.137    If ``pass_many=True``, the raw data (which may be a collection) is passed.138 139    .. versionchanged:: 3.0.0140        ``many`` is always passed as a keyword arguments to the decorated method.141    """142    return set_hook(fn, PRE_DUMP, many=pass_many)143 144 145def post_dump(146    fn: Callable[..., Any] | None = None,147    pass_many: bool = False,  # noqa: FBT001, FBT002148    pass_original: bool = False,  # noqa: FBT001, FBT002149) -> Callable[..., Any]:150    """Register a method to invoke after serializing an object. The method151    receives the serialized object and returns the processed object.152 153    By default it receives a single object at a time, transparently handling the ``many``154    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.dump` call.155    If ``pass_many=True``, the raw data (which may be a collection) is passed.156 157    If ``pass_original=True``, the original data (before serializing) will be passed as158    an additional argument to the method.159 160    .. versionchanged:: 3.0.0161        ``many`` is always passed as a keyword arguments to the decorated method.162    """163    return set_hook(fn, POST_DUMP, many=pass_many, pass_original=pass_original)164 165 166def pre_load(167    fn: Callable[..., Any] | None = None,168    pass_many: bool = False,  # noqa: FBT001, FBT002169) -> Callable[..., Any]:170    """Register a method to invoke before deserializing an object. The method171    receives the data to be deserialized and returns the processed data.172 173    By default it receives a single object at a time, transparently handling the ``many``174    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.load` call.175    If ``pass_many=True``, the raw data (which may be a collection) is passed.176 177    .. versionchanged:: 3.0.0178        ``partial`` and ``many`` are always passed as keyword arguments to179        the decorated method.180    """181    return set_hook(fn, PRE_LOAD, many=pass_many)182 183 184def post_load(185    fn: Callable[..., Any] | None = None,186    pass_many: bool = False,  # noqa: FBT001, FBT002187    pass_original: bool = False,  # noqa: FBT001, FBT002188) -> Callable[..., Any]:189    """Register a method to invoke after deserializing an object. The method190    receives the deserialized data and returns the processed data.191 192    By default it receives a single object at a time, transparently handling the ``many``193    argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.load` call.194    If ``pass_many=True``, the raw data (which may be a collection) is passed.195 196    If ``pass_original=True``, the original data (before deserializing) will be passed as197    an additional argument to the method.198 199    .. versionchanged:: 3.0.0200        ``partial`` and ``many`` are always passed as keyword arguments to201        the decorated method.202    """203    return set_hook(fn, POST_LOAD, many=pass_many, pass_original=pass_original)204 205 206def set_hook(207    fn: Callable[..., Any] | None,208    tag: str,209    many: bool = False,  # noqa: FBT001, FBT002210    **kwargs: Any,211) -> Callable[..., Any]:212    """Mark decorated function as a hook to be picked up later.213    You should not need to use this method directly.214 215    .. note::216        Currently only works with functions and instance methods. Class and217        static methods are not supported.218 219    :return: Decorated function if supplied, else this decorator with its args220        bound.221    """222    # Allow using this as either a decorator or a decorator factory.223    if fn is None:224        return functools.partial(set_hook, tag=tag, many=many, **kwargs)225 226    # Set a __marshmallow_hook__ attribute instead of wrapping in some class,227    # because I still want this to end up as a normal (unbound) method.228    function = cast(MarshmallowHook, fn)229    try:230        hook_config = function.__marshmallow_hook__231    except AttributeError:232        function.__marshmallow_hook__ = hook_config = defaultdict(list)233    # Also save the kwargs for the tagged function on234    # __marshmallow_hook__, keyed by <tag>235    if hook_config is not None:236        hook_config[tag].append((many, kwargs))237 238    return fn239 
codekingpro/portable-devtools · Team Ai