Skip to content

Commit 08e1950

Browse files
committed
Replace propagate_unknown with or-able unknown
Rather than having a new parameter which allows `propagate_unknown` behavior, convert this into an option which can be set on the ``unknown`` parameter itself. Drawing from other interfaces which present flags as or-able values (traditionally bitmasks), express the setting of this option as `x | PROPAGATE` for any supported `x`. This makes it harder to propagate the value of `propagate` separately from the rest of the value of `unknown`. For simplicity, change the behavior here to suit this implementation.
1 parent fc6f52a commit 08e1950

File tree

8 files changed

+122
-140
lines changed

8 files changed

+122
-140
lines changed

CHANGELOG.rst

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,26 @@ Changelog
66

77
Features:
88

9-
- The behavior of the ``unknown`` option can be further customized with a
10-
second option, ``propagate_unknown``. When set to ``True``, any nested
11-
schemas will receive the same ``unknown`` value as their parent if they did
12-
not set an ``unknown`` value explicitly.
13-
``propagate_unknown`` itself propagates across any number of layers of
14-
nesting and cannot be disabled by a child schema if it enabled in its parent.
9+
- The behavior of the ``unknown`` option can be further customized with a new
10+
value, ``PROPAGATE``. If ``unknown=EXCLUDE | PROPAGATE`` is set, then the
11+
value of ``unknown=EXCLUDE | PROPAGATE`` will be passed to any nested
12+
schemas which do not explicitly set ``unknown`` in ``Nested`` or schema
13+
options. This works for ``INCLUDE | PROPAGATE`` and ``RAISE | PROPAGATE`` as
14+
well.
1515
(:issue:`1490`, :issue:`1428`)
1616
Thanks :user:`lmignon` and :user:`mahenzon`.
1717

1818
.. note::
1919

20-
In order to retain flexibility, when a schema is being loaded with
21-
``propagate_unknown=True``, you can still set ``unknown`` explicitly on child
22-
schemas. However, be aware that such a value will be propagated to any of its
23-
descendants.
20+
When a schema is being loaded with ``unknown=... | PROPAGATE``, you can still
21+
set ``unknown`` explicitly on child schemas. However, be aware that such a
22+
value may turn off propagation at that point in the schema hierarchy.
2423

2524
For example, a schema which specifies ``unknown=EXCLUDE`` will set
26-
``EXCLUDE`` in all of its children. If one of the children specifies
27-
``unknown=IGNORE``, then ``IGNORE`` will be passed to the relevant
28-
grandchildren.
29-
Other children of the original schema could specify ``unknown=RAISE``, and
30-
this would apply to them equally well.
25+
``EXCLUDE`` for itself. But because the value is ``EXCLUDE`` rather than
26+
``EXCLUDE | PROPAGATE``, that setting will not be propagated to its
27+
children, even if there is a parent schema which sets
28+
``unknown=EXCLUDE | PROPAGATE``.
3129

3230
3.7.0 (2020-07-08)
3331
******************

src/marshmallow/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
validates,
1010
validates_schema,
1111
)
12-
from marshmallow.utils import EXCLUDE, INCLUDE, RAISE, pprint, missing
12+
from marshmallow.utils import EXCLUDE, INCLUDE, RAISE, PROPAGATE, pprint, missing
1313
from marshmallow.exceptions import ValidationError
1414
from distutils.version import LooseVersion
1515

@@ -19,6 +19,7 @@
1919
"EXCLUDE",
2020
"INCLUDE",
2121
"RAISE",
22+
"PROPAGATE",
2223
"Schema",
2324
"SchemaOpts",
2425
"fields",

src/marshmallow/base.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,11 @@ def dumps(self, obj, *, many: bool = None):
3939
raise NotImplementedError
4040

4141
def load(
42-
self,
43-
data,
44-
*,
45-
many: bool = None,
46-
partial=None,
47-
unknown=None,
48-
propagate_unknown=None
42+
self, data, *, many: bool = None, partial=None, unknown=None
4943
):
5044
raise NotImplementedError
5145

5246
def loads(
53-
self,
54-
json_data,
55-
*,
56-
many: bool = None,
57-
partial=None,
58-
unknown=None,
59-
propagate_unknown=None,
60-
**kwargs
47+
self, json_data, *, many: bool = None, partial=None, unknown=None, **kwargs
6148
):
6249
raise NotImplementedError

src/marshmallow/fields.py

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
missing as missing_,
1919
resolve_field_instance,
2020
is_aware,
21+
UnknownParam,
2122
)
2223
from marshmallow.exceptions import (
2324
ValidationError,
@@ -459,10 +460,6 @@ class ParentSchema(Schema):
459460
:param many: Whether the field is a collection of objects.
460461
:param unknown: Whether to exclude, include, or raise an error for unknown
461462
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
462-
:param propagate_unknown: If ``True``, the value for ``unknown`` will be
463-
applied to any ``Nested`` fields within the nested schema. Propagates down and allows
464-
``Nested`` fields to apply their own ``unknown`` value. Note that this
465-
only has an effect when there are multiple layers of nesting
466463
:param kwargs: The same keyword arguments that :class:`Field` receives.
467464
"""
468465

@@ -477,8 +474,7 @@ def __init__(
477474
only: types.StrSequenceOrSet = None,
478475
exclude: types.StrSequenceOrSet = (),
479476
many: bool = False,
480-
unknown: str = None,
481-
propagate_unknown: bool = None,
477+
unknown: typing.Union[str, UnknownParam] = None,
482478
**kwargs
483479
):
484480
# Raise error if only or exclude is passed as string, not list of strings
@@ -498,8 +494,7 @@ def __init__(
498494
self.only = only
499495
self.exclude = exclude
500496
self.many = many
501-
self.unknown = unknown
502-
self.propagate_unknown = propagate_unknown
497+
self.unknown = UnknownParam.parse_if_str(unknown) if unknown else None
503498
self._schema = None # Cached Schema instance
504499
super().__init__(default=default, **kwargs)
505500

@@ -577,32 +572,18 @@ def _test_collection(self, value):
577572
if many and not utils.is_collection(value):
578573
raise self.make_error("type", input=value, type=value.__class__.__name__)
579574

580-
def _load(self, value, data, partial=None, unknown=None, propagate_unknown=None):
575+
def _load(self, value, data, partial=None, unknown=None):
581576
try:
582577
valid_data = self.schema.load(
583-
value,
584-
unknown=unknown or self.unknown,
585-
propagate_unknown=propagate_unknown
586-
if propagate_unknown is not None
587-
else self.propagate_unknown,
588-
partial=partial,
578+
value, unknown=unknown if unknown else self.unknown, partial=partial,
589579
)
590580
except ValidationError as error:
591581
raise ValidationError(
592582
error.messages, valid_data=error.valid_data
593583
) from error
594584
return valid_data
595585

596-
def _deserialize(
597-
self,
598-
value,
599-
attr,
600-
data,
601-
partial=None,
602-
unknown=None,
603-
propagate_unknown=None,
604-
**kwargs
605-
):
586+
def _deserialize(self, value, attr, data, partial=None, unknown=None, **kwargs):
606587
"""Same as :meth:`Field._deserialize` with additional ``partial`` argument.
607588
608589
:param bool|tuple partial: For nested schemas, the ``partial``
@@ -616,18 +597,14 @@ def _deserialize(
616597
# however, we should only respect `self.schema.unknown` if
617598
# `auto_unknown` is False, meaning that it was set explicitly on the
618599
# schema class or instance
619-
explicit_unknown = self.unknown or (
620-
self.schema.unknown if not self.schema.auto_unknown else None
600+
explicit_unknown = (
601+
self.unknown
602+
if self.unknown
603+
else (self.schema.unknown if not self.schema.auto_unknown else None)
621604
)
622605
if explicit_unknown:
623606
unknown = explicit_unknown
624-
return self._load(
625-
value,
626-
data,
627-
partial=partial,
628-
unknown=unknown,
629-
propagate_unknown=propagate_unknown,
630-
)
607+
return self._load(value, data, partial=partial, unknown=unknown,)
631608

632609

633610
class Pluck(Nested):

0 commit comments

Comments
 (0)