Skip to content

Commit 3391b69

Browse files
authored
Merge pull request #541 from sirosen/errorhandler-must-reraise
[7.x] ValueError if error handler does not reraise
2 parents e62f478 + 4fe8b76 commit 3391b69

4 files changed

Lines changed: 41 additions & 8 deletions

File tree

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ Usages are varied, but include
4545
4646
parser = MyParser()
4747
48+
Changes:
49+
50+
* Registered `error_handler` callbacks are required to raise an exception.
51+
If a handler is invoked and no exception is raised, `webargs` will raise
52+
a `ValueError` (:issue:`527`)
53+
4854
6.1.1 (2020-09-08)
4955
******************
5056

src/webargs/core.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,9 @@ def parse(
283283
error_status_code=error_status_code,
284284
error_headers=error_headers,
285285
)
286-
warnings.warn(
287-
"_on_validation_error hook did not raise an exception and flow "
288-
"of control returned to parse(). You may get unexpected results"
289-
)
286+
raise ValueError(
287+
"_on_validation_error hook did not raise an exception"
288+
) from error
290289
return data
291290

292291
def get_default_request(self):

tests/test_core.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,19 @@ def test_value_error_raised_if_parse_called_with_invalid_location(parser, web_re
289289

290290
@mock.patch("webargs.core.Parser.handle_error")
291291
def test_handle_error_called_when_parsing_raises_error(handle_error, web_request):
292+
# handle_error must raise an error to be valid
293+
handle_error.side_effect = ValidationError("parsing failed")
294+
292295
def always_fail(*args, **kwargs):
293296
raise ValidationError("error occurred")
294297

295298
p = Parser()
296299
assert handle_error.call_count == 0
297-
p.parse({"foo": fields.Field()}, web_request, validate=always_fail)
300+
with pytest.raises(ValidationError):
301+
p.parse({"foo": fields.Field()}, web_request, validate=always_fail)
298302
assert handle_error.call_count == 1
299-
p.parse({"foo": fields.Field()}, web_request, validate=always_fail)
303+
with pytest.raises(ValidationError):
304+
p.parse({"foo": fields.Field()}, web_request, validate=always_fail)
300305
assert handle_error.call_count == 2
301306

302307

@@ -360,6 +365,25 @@ def handle_error(error, req, schema, *, error_status_code, error_headers):
360365
parser.parse(mock_schema, web_request)
361366

362367

368+
def test_custom_error_handler_must_reraise(web_request):
369+
class CustomError(Exception):
370+
pass
371+
372+
mock_schema = mock.Mock(spec=Schema)
373+
mock_schema.strict = True
374+
mock_schema.load.side_effect = ValidationError("parsing json failed")
375+
parser = Parser()
376+
377+
@parser.error_handler
378+
def handle_error(error, req, schema, *, error_status_code, error_headers):
379+
pass
380+
381+
# because the handler above does not raise a new error, the parser should
382+
# raise a ValueError -- indicating a programming error
383+
with pytest.raises(ValueError):
384+
parser.parse(mock_schema, web_request)
385+
386+
363387
def test_custom_location_loader(web_request):
364388
web_request.data = {"foo": 42}
365389

tests/test_flaskparser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from werkzeug.exceptions import HTTPException
1+
from werkzeug.exceptions import HTTPException, BadRequest
22
import pytest
33

44
from flask import Flask
@@ -85,6 +85,9 @@ def test_parsing_unexpected_headers_when_raising(self, testapp):
8585

8686
@mock.patch("webargs.flaskparser.abort")
8787
def test_abort_called_on_validation_error(mock_abort):
88+
# error handling must raise an error to be valid
89+
mock_abort.side_effect = BadRequest("foo")
90+
8891
app = Flask("testapp")
8992

9093
def validate(x):
@@ -97,7 +100,8 @@ def validate(x):
97100
data=json.dumps({"value": 41}),
98101
content_type="application/json",
99102
):
100-
parser.parse(argmap)
103+
with pytest.raises(HTTPException):
104+
parser.parse(argmap)
101105
mock_abort.assert_called()
102106
abort_args, abort_kwargs = mock_abort.call_args
103107
assert abort_args[0] == 422

0 commit comments

Comments
 (0)