Repository: marshmallow-code/webargs

Issue:
Unused `status_code` field in `ValidationError`?
I'm trying to figure out how to return specific status codes from validation errors. Looks like it always returns a `422`, ignoring whats specified in the `ValidationError`.

``` python
from flask import Flask
from webargs import fields, ValidationError
from webargs.flaskparser import use_args

app = Flask(__name__)

def dummy_validator(val):
    raise ValidationError(message="Error!", status_code=500)

@app.route('/')
@use_args({'test_field': fields.String(validate=dummy_validator)})
def hello_world(args):
    return 'Hello World!'

if __name__ == '__main__':
    app.run('0.0.0.0', debug=True)
```

Then: `curl -v http://localhost:5000?test_field=hello` returns `422 Unprocessable Entity` instead of `500 Internal Server Error`.



Discussion:
This is a known bug. Since marshmallow catches `ValidationErrors`, collects and formats them, then reraises _a new_ `ValidationError`, there is currently no way to use the `status_code` attribute in a webargs Parser's `error_callback`.

I welcome any help and ideas on how to address this.


Resolving patch:
diff --git a/webargs/core.py b/webargs/core.py
--- a/webargs/core.py
+++ b/webargs/core.py
@@ -45,7 +45,9 @@ class ValidationError(WebargsError, ma.exceptions.ValidationError):
     def __init__(self, message, status_code=DEFAULT_VALIDATION_STATUS, headers=None, **kwargs):
         self.status_code = status_code
         self.headers = headers
-        ma.exceptions.ValidationError.__init__(self, message, **kwargs)
+        ma.exceptions.ValidationError.__init__(
+            self, message, status_code=status_code, headers=headers, **kwargs
+        )
 
     def __repr__(self):
         return 'ValidationError({0!r}, status_code={1}, headers={2})'.format(
@@ -290,11 +292,10 @@ def _on_validation_error(self, error):
             # Raise a webargs error instead
             error = ValidationError(
                 error.messages,
-                status_code=getattr(error, 'status_code', self.DEFAULT_VALIDATION_STATUS),
-                headers=getattr(error, 'headers', {}),
                 field_names=error.field_names,
                 fields=error.fields,
-                data=error.data
+                data=error.data,
+                **getattr(error, 'kwargs', {})
             )
         if self.error_callback:
             self.error_callback(error)


Test patch:
diff --git a/tests/test_flaskparser.py b/tests/test_flaskparser.py
--- a/tests/test_flaskparser.py
+++ b/tests/test_flaskparser.py
@@ -8,11 +8,14 @@
 from flask.views import MethodView
 from werkzeug.exceptions import HTTPException
 from werkzeug.datastructures import ImmutableMultiDict
+import marshmallow
 import pytest
 
 from webargs import fields, ValidationError, missing
 from webargs.flaskparser import FlaskParser, use_args, use_kwargs, abort
 
+MARSHMALLOW_VERSION_INFO = marshmallow.__version__.split('.')
+
 class TestAppConfig:
     TESTING = True
     DEBUG = True
@@ -471,3 +474,17 @@ def always_fail(value):
             parser.parse(args, locations=('json', ))
     exc = excinfo.value
     assert exc.code == 422
+
+# Regression test for https://github.com/sloria/webargs/issues/85
+@pytest.mark.skipif(int(MARSHMALLOW_VERSION_INFO[1]) < 7,
+                    reason='status_code only works in marshmallow>=2.7')
+def test_field_validation_error_with_custom_status_code(testapp):
+    def always_fail(value):
+        raise ValidationError('something went wrong', status_code=400)
+    args = {'text': fields.Field(validate=always_fail)}
+    with testapp.test_request_context('/foo', data=json.dumps({'text': 'bar'}),
+            content_type='application/json', method='POST'):
+        with pytest.raises(HTTPException) as excinfo:
+            parser.parse(args, locations=('json', ))
+    exc = excinfo.value
+    assert exc.code == 400