diff --git a/src/marshmallow/experimental/meta.py b/src/marshmallow/experimental/meta.py new file mode 100644 index 000000000..50fede562 --- /dev/null +++ b/src/marshmallow/experimental/meta.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import typing + +if typing.TYPE_CHECKING: + from marshmallow.fields import Field + from marshmallow.schema import SchemaMeta + from marshmallow.types import UnknownOption + + +@typing.overload +def meta( + *bases: SchemaMeta, + fields: tuple[str, ...] | list[str] | None, + additional: tuple[str, ...] | list[str] | None, + include: dict[str, Field] | None, + exclude: tuple[str, ...] | list[str] | None, + many: bool | None, + dateformat: str | None, + datetimeformat: str | None, + timeformat: str | None, + render_module: typing.Any | None, + index_errors: bool | None, + load_only: tuple[str, ...] | list[str] | None, + dump_only: tuple[str, ...] | list[str] | None, + unknown: UnknownOption | None, + register: bool | None, + **kwargs, +): + """ + :param *bases: The meta classes to inherit from. Inherits from the decorated schema's + Meta class by default. Pass `None` to prevent inheritance. + :param fields: Fields to include in the (de)serialized result + :param additional: Fields to include in addition to the explicitly declared fields. + `additional ` and `fields ` + are mutually-exclusive options. + :param include: Dictionary of additional fields to include in the schema. It is + usually better to define fields as class variables, but you may need to + use this option, e.g., if your fields are Python keywords. + :param exclude: Fields to exclude in the serialized result. + Nested fields can be represented with dot delimiters. + :param many: Whether data should be (de)serialized as a collection by default. + :param dateformat: Default format for `Date ` fields. + :param datetimeformat: Default format for `DateTime ` fields. + :param timeformat: Default format for `Time ` fields. + :param render_module: Module to use for `loads ` and `dumps `. + Defaults to `json` from the standard library. + :param index_errors: If `True`, errors dictionaries will include the index of invalid items in a collection. + :param load_only: Fields to exclude from serialized results + :param dump_only: Fields to exclude from serialized results + :param unknown: Whether to exclude, include, or raise an error for unknown fields in the data. + Use `EXCLUDE`, `INCLUDE` or `RAISE`. + :param register: Whether to register the `Schema ` with marshmallow's internal + class registry. Must be `True` if you intend to refer to this `Schema ` + by class name in `Nested` fields. Only set this to `False` when memory + usage is critical. Defaults to `True`. + """ + + +@typing.overload +def meta(*bases, **kwargs): ... + + +def meta(*bases, **kwargs): + def wrapper(schema): + mro = bases if bases else (schema.Meta,) + meta = type(schema.Meta.__name__, mro, kwargs) + return type(schema.__name__, (schema,), {"Meta": meta}) + + return wrapper diff --git a/tests/test_meta.py b/tests/test_meta.py new file mode 100644 index 000000000..2bd60e6a1 --- /dev/null +++ b/tests/test_meta.py @@ -0,0 +1,37 @@ +from marshmallow import Schema +from marshmallow.experimental.meta import meta + + +class Base(Schema): + class Meta: + foo = True + + +class TestMeta: + def test_default_inheritance(self): + @meta(bar=True) + class Test(Base): + pass + + assert getattr(Test.Meta, "foo", None) + assert getattr(Test.Meta, "bar", None) + + def test_explicit_inheritance(self): + class Parent(Schema): + class Meta: + bar = True + + @meta(Base.Meta, Parent.Meta, baz=True) + class Test(Schema): + pass + + assert getattr(Test.Meta, "foo", None) + assert getattr(Test.Meta, "bar", None) + assert getattr(Test.Meta, "baz", None) + + def test_clear_inheritance(self): + @meta(Schema.Meta, bar=True) + class Test(Base): + pass + + assert not hasattr(Test.Meta, "foo")