Skip to content

Commit 205791e

Browse files
committed
Remove Parser.DEFAULT_UNKNOWN to reduce complexity
Removing this value removes a layer of defaults and potential ambiguity from the interfaces. Docs and changelog update to remove all references to DEFAULT_UNKNOWN.
1 parent 8278374 commit 205791e

5 files changed

Lines changed: 38 additions & 43 deletions

File tree

CHANGELOG.rst

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ Refactoring:
2727
2828
Features:
2929

30-
* Add ``unknown`` as a parameter to ``Parser.parse``, ``Parser.use_args``, and
31-
``Parser.use_kwargs``, or to parser instantiation. When set, it will be passed
30+
* Add ``unknown`` as a parameter to ``Parser.parse``, ``Parser.use_args``,
31+
``Parser.use_kwargs``, and parser instantiation. When set, it will be passed
3232
to ``Schema.load``. When not set, the value passed will depend on the parser's
3333
settings. If set to ``None``, the schema's default behavior will be used (i.e.
3434
no value is passed to ``Schema.load``) and parser settings will be ignored.
@@ -48,8 +48,7 @@ This allows usages like
4848
4949
* Defaults for ``unknown`` may be customized on parser classes via
5050
``Parser.DEFAULT_UNKNOWN_BY_LOCATION``, which maps location names to values
51-
to use, and ``Parser.DEFAULT_UNKNOWN``, which is used when a location is not
52-
found in ``DEFAULT_UNKNOWN_BY_LOCATION``.
51+
to use.
5352

5453
Usages are varied, but include
5554

@@ -58,8 +57,6 @@ Usages are varied, but include
5857
import marshmallow as ma
5958
from webargs.flaskparser import FlaskParser
6059
61-
parser = FlaskParser(unknown=ma.INCLUDE)
62-
6360
# as well as...
6461
class MyParser(FlaskParser):
6562
DEFAULT_UNKNOWN_BY_LOCATION = {"query": ma.INCLUDE}
@@ -78,7 +75,7 @@ will always pass ``RAISE``, even when the location is ``query``.
7875
* By default, webargs will pass ``unknown=EXCLUDE`` for all locations except
7976
for request bodies (``json``, ``form``, and ``json_or_form``) and path
8077
parameters. Request bodies and path parameters will pass ``unknown=RAISE``.
81-
This behavior is defined by the default values for ``DEFAULT_UNKNOWN`` and
78+
This behavior is defined by the default value for
8279
``DEFAULT_UNKNOWN_BY_LOCATION``.
8380

8481
Changes:

docs/advanced.rst

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,30 +151,24 @@ Default `unknown`
151151
+++++++++++++++++
152152

153153
By default, webargs will pass `unknown=marshmallow.EXCLUDE` except when the
154-
location is `json`, `form`, or `json_or_form`. In those cases, it uses
155-
`unknown=marshmallow.RAISE` instead.
154+
location is `json`, `form`, `json_or_form`, `path`, or `path`. In those cases,
155+
it uses `unknown=marshmallow.RAISE` instead.
156156

157-
You can change these defaults by overriding `DEFAULT_UNKNOWN_BY_LOCATION` and
158-
`DEFAULT_UNKNOWN`. The first is a mapping of locations to values to pass, and
159-
the second is the fallback value used if the location is not included in the
160-
map.
161-
162-
You can also define a default at parser instantiation, which will take
163-
precedence over these defaults.
157+
You can change these defaults by overriding `DEFAULT_UNKNOWN_BY_LOCATION`.
158+
This is a mapping of locations to values to pass.
164159

165160
For example,
166161

167162
.. code-block:: python
168163
169164
from flask import Flask
170-
from marshmallow import EXCLUDE, INCLUDE, fields
165+
from marshmallow import EXCLUDE, fields
171166
from webargs.flaskparser import FlaskParser
172167
173168
app = Flask(__name__)
174169
175170
176171
class Parser(FlaskParser):
177-
DEFAULT_UNKNOWN = INCLUDE
178172
DEFAULT_UNKNOWN_BY_LOCATION = {"query": EXCLUDE}
179173
180174
@@ -190,14 +184,30 @@ For example,
190184
191185
192186
# location is "json", which is not in DEFAULT_UNKNOWN_BY_LOCATION,
193-
# so the parser's default value, `INCLUDE`, will be used
187+
# so no value will be passed for `unknown`
188+
@app.route("/", methods=["POST"])
189+
@parser.use_args({"foo": fields.Int(), "bar": fields.Int()}, location="json")
190+
def post(self, args):
191+
return f"foo x bar = {args['foo'] * args['bar']}"
192+
193+
194+
You can also define a default at parser instantiation, which will take
195+
precedence over these defaults, as in
196+
197+
.. code-block:: python
198+
199+
from marshmallow import INCLUDE
200+
201+
parser = Parser(unknown=INCLUDE)
202+
203+
# because `unknown` is set on the parser, `DEFAULT_UNKNOWN_BY_LOCATION` has
204+
# effect and `INCLUDE` will always be used
194205
@app.route("/", methods=["POST"])
195206
@parser.use_args({"foo": fields.Int(), "bar": fields.Int()}, location="json")
196207
def post(self, args):
197208
unexpected_args = [k for k in args.keys() if k not in ("foo", "bar")]
198209
return f"foo x bar = {args['foo'] * args['bar']}; unexpected args={unexpected_args}"
199210
200-
201211
Using Schema-Specfied `unknown`
202212
+++++++++++++++++++++++++++++++
203213

@@ -230,9 +240,8 @@ If you wish to use the value of `unknown` specified by a schema, simply pass
230240
return f"area = {args['length'] * args['width']}"
231241
232242
233-
You can also set ``unknown=None`` when instantiating a parser, or set
234-
``DEFAULT_UNKNOWN = None`` to make this behavior the default for a parser
235-
class.
243+
You can also set ``unknown=None`` when instantiating a parser to make this
244+
behavior the default for a parser.
236245

237246

238247
When to avoid `use_kwargs`

src/webargs/asyncparser.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ async def parse(
4545
else (
4646
self.unknown
4747
if self.unknown != core._UNKNOWN_DEFAULT_PARAM
48-
else self.DEFAULT_UNKNOWN_BY_LOCATION.get(
49-
location, self.DEFAULT_UNKNOWN
50-
)
48+
else self.DEFAULT_UNKNOWN_BY_LOCATION.get(location)
5149
)
5250
)
5351
load_kwargs = {"unknown": unknown}

src/webargs/core.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,16 @@ class Parser:
111111
#: Default location to check for data
112112
DEFAULT_LOCATION = "json"
113113
#: Default value to use for 'unknown' on schema load
114-
DEFAULT_UNKNOWN = ma.EXCLUDE
115-
#: per-location default for 'unknown'
114+
# on a per-location basis
116115
DEFAULT_UNKNOWN_BY_LOCATION = {
117116
"json": ma.RAISE,
118117
"form": ma.RAISE,
119118
"json_or_form": ma.RAISE,
119+
"querystring": ma.EXCLUDE,
120+
"query": ma.EXCLUDE,
121+
"headers": ma.EXCLUDE,
122+
"cookies": ma.EXCLUDE,
123+
"files": ma.EXCLUDE,
120124
}
121125
#: The marshmallow Schema class to use when creating new schemas
122126
DEFAULT_SCHEMA_CLASS = ma.Schema
@@ -267,16 +271,14 @@ def parse(
267271
"""
268272
req = req if req is not None else self.get_default_request()
269273
location = location or self.location
270-
# precedence order: explicit, instance setting, default per location, default
274+
# precedence order: explicit, instance setting, default per location
271275
unknown = (
272276
unknown
273277
if unknown != _UNKNOWN_DEFAULT_PARAM
274278
else (
275279
self.unknown
276280
if self.unknown != _UNKNOWN_DEFAULT_PARAM
277-
else self.DEFAULT_UNKNOWN_BY_LOCATION.get(
278-
location, self.DEFAULT_UNKNOWN
279-
)
281+
else self.DEFAULT_UNKNOWN_BY_LOCATION.get(location)
280282
)
281283
)
282284
load_kwargs = {"unknown": unknown} if unknown else {}

tests/test_core.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ def test_parse(parser, web_request):
114114
"parse_call",
115115
"parser_default",
116116
"parser_class_default",
117-
"parser_class_location_default",
118117
],
119118
)
120119
def test_parse_with_unknown_behavior_specified(parser, web_request, set_location):
@@ -142,14 +141,6 @@ def parse_with_desired_behavior(value):
142141
elif set_location == "parser_class_default":
143142

144143
class CustomParser(MockRequestParser):
145-
DEFAULT_UNKNOWN = value
146-
DEFAULT_UNKNOWN_BY_LOCATION = {}
147-
148-
return CustomParser().parse(CustomSchema(), web_request)
149-
elif set_location == "parser_class_location_default":
150-
151-
class CustomParser(MockRequestParser):
152-
DEFAULT_UNKNOWN = None
153144
DEFAULT_UNKNOWN_BY_LOCATION = {"json": value}
154145

155146
return CustomParser().parse(CustomSchema(), web_request)
@@ -204,7 +195,6 @@ class CustomSchema(Schema):
204195
if clear_method == "custom_class":
205196

206197
class CustomParser(MockRequestParser):
207-
DEFAULT_UNKNOWN = None
208198
DEFAULT_UNKNOWN_BY_LOCATION = {}
209199

210200
parser = CustomParser()
@@ -213,7 +203,6 @@ class CustomParser(MockRequestParser):
213203
elif clear_method == "both":
214204
# setting things in multiple ways should not result in errors
215205
class CustomParser(MockRequestParser):
216-
DEFAULT_UNKNOWN = None
217206
DEFAULT_UNKNOWN_BY_LOCATION = {}
218207

219208
parser = CustomParser(unknown=None)

0 commit comments

Comments
 (0)