-
-
Notifications
You must be signed in to change notification settings - Fork 640
Meta Decorator #2810
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Meta Decorator #2810
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -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 <marshmallow.Schema.Meta.additional>` and `fields <marshmallow.Schema.Meta.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 <marshmallow.fields.Date>` fields. | ||||||||||||||||||||||||||
:param datetimeformat: Default format for `DateTime <marshmallow.fields.DateTime>` fields. | ||||||||||||||||||||||||||
:param timeformat: Default format for `Time <marshmallow.fields.Time>` fields. | ||||||||||||||||||||||||||
:param render_module: Module to use for `loads <marshmallow.Schema.loads>` and `dumps <marshmallow.Schema.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 <marshmallow.Schema>` with marshmallow's internal | ||||||||||||||||||||||||||
class registry. Must be `True` if you intend to refer to this `Schema <marshmallow.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): | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the marshmallow/src/marshmallow/fields.py Lines 87 to 98 in f0c6afb
or perhaps def meta(
*bases: SchemaMeta,
fields: tuple[str, ...] | list[str] | None,
additional: tuple[str, ...] | list[str] | None,
# ...
**kwargs,
):
def wrapper(schema):
mro = bases if bases else (schema.Meta,)
meta = type(schema.Meta.__name__, mro, {
"fields": fields,
"additional": additional,
# ...
**kwargs
})
return type(schema.__name__, (schema,), {"Meta": meta})
return wrapper There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Explicitly copying the args into a dictionary defines missing attributes as One There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I see. In that case, maybe just add comments about why this approach is necessary. Could even include a link to that gh comment |
||||||||||||||||||||||||||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be
? Surprised mypy doesn't raise an error in the tests tho 🤔