prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  ValidationError causes HTTP/500 in Flask instead of HTTP/422 in Python 2
  Here are the symptoms: http://stackoverflow.com/questions/37321835/flask-error-handler-not-able-to-handle-assertion-error/39624893#39624893
  
  Here is the case where I personally hit the issue with Python 2.x and PyPy in Python 2.x mode only: https://travis-ci.org/frol/flask-restplus-server-example/builds/161685419
  
  After troubleshooting, I discovered that this issue stems from the `FlaskParser.handle_error` which calls `abort` function with `exc=` argument, which then [is set as "data"](https://github.com/sloria/webargs/blob/dev/webargs/flaskparser.py#L38) on the raised `HTTPException`, which in its turn is tried to be json-dumped and fails with `TypeError`, and now the new exception is in `sys.exc_info()`, which causes Flask to [assert here](https://github.com/pallets/flask/blob/01081dbe6cdfa3fc43d8e1fff708d4ed95e1be7e/flask/app.py#L1515).
  
  Searching git history for the introduced `exc=` in `FlaskParser`, I found it here: https://github.com/sloria/webargs/commit/6f8088c7c85c05d2891e81ee97d38f2ae801159d
  
  Is there a reason for `exc=` to be passed? How can we fix this?
  
  Python 3 seems to have changed something with `sys.exc_info()` behaviour since even after the `TypeError` caused by `json.dumps()`, `sys.exc_info()` still reports `HTTPException` while in Python 2 it returns `TypeError`.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `webargs/flaskparser.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 """Flask request argument parsing module.
  3 
  4 Example: ::
  5 
  6     from flask import Flask
  7 
  8     from webargs import fields
  9     from webargs.flaskparser import use_args
  10 
  11     app = Flask(__name__)
  12 
  13     hello_args = {
  14         'name': fields.Str(required=True)
  15     }
  16 
  17     @app.route('/')
  18     @use_args(hello_args)
  19     def index(args):
  20         return 'Hello ' + args['name']
  21 """
  22 import flask
  23 from werkzeug.exceptions import HTTPException
  24 
  25 from webargs import core
  26 
  27 
  28 def abort(http_status_code, **kwargs):
  29     """Raise a HTTPException for the given http_status_code. Attach any keyword
  30     arguments to the exception for later processing.
  31 
  32     From Flask-Restful. See NOTICE file for license information.
  33     """
  34     try:
  35         flask.abort(http_status_code)
  36     except HTTPException as err:
  37         if len(kwargs):
  38             err.data = kwargs
  39         raise err
  40 
  41 
  42 def is_json_request(req):
  43     return core.is_json(req.mimetype)
  44 
  45 
  46 class FlaskParser(core.Parser):
  47     """Flask request argument parser."""
  48 
  49     __location_map__ = dict(
  50         view_args='parse_view_args',
  51         **core.Parser.__location_map__
  52     )
  53 
  54     def parse_view_args(self, req, name, field):
  55         """Pull a value from the request's ``view_args``."""
  56         return core.get_value(req.view_args, name, field)
  57 
  58     def parse_json(self, req, name, field):
  59         """Pull a json value from the request."""
  60         # Pass force in order to handle vendor media types,
  61         # e.g. applications/vnd.json+api
  62         # this should be unnecessary in Flask 1.0
  63         force = is_json_request(req)
  64         # Fail silently so that the webargs parser can handle the error
  65         if hasattr(req, 'get_json'):
  66             # Flask >= 0.10.x
  67             json_data = req.get_json(force=force, silent=True)
  68         else:
  69             # Flask <= 0.9.x
  70             json_data = req.json
  71         if json_data is None:
  72             return core.missing
  73         return core.get_value(json_data, name, field, allow_many_nested=True)
  74 
  75     def parse_querystring(self, req, name, field):
  76         """Pull a querystring value from the request."""
  77         return core.get_value(req.args, name, field)
  78 
  79     def parse_form(self, req, name, field):
  80         """Pull a form value from the request."""
  81         try:
  82             return core.get_value(req.form, name, field)
  83         except AttributeError:
  84             pass
  85         return core.missing
  86 
  87     def parse_headers(self, req, name, field):
  88         """Pull a value from the header data."""
  89         return core.get_value(req.headers, name, field)
  90 
  91     def parse_cookies(self, req, name, field):
  92         """Pull a value from the cookiejar."""
  93         return core.get_value(req.cookies, name, field)
  94 
  95     def parse_files(self, req, name, field):
  96         """Pull a file from the request."""
  97         return core.get_value(req.files, name, field)
  98 
  99     def handle_error(self, error):
  100         """Handles errors during parsing. Aborts the current HTTP request and
  101         responds with a 422 error.
  102         """
  103         status_code = getattr(error, 'status_code', self.DEFAULT_VALIDATION_STATUS)
  104         abort(status_code, messages=error.messages, exc=error)
  105 
  106     def get_default_request(self):
  107         """Override to use Flask's thread-local request objec by default"""
  108         return flask.request
  109 
  110 parser = FlaskParser()
  111 use_args = parser.use_args
  112 use_kwargs = parser.use_kwargs
  113
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/webargs/flaskparser.py b/webargs/flaskparser.py
  --- a/webargs/flaskparser.py
  +++ b/webargs/flaskparser.py
  @@ -25,7 +25,7 @@
   from webargs import core
   
   
  -def abort(http_status_code, **kwargs):
  +def abort(http_status_code, exc=None, **kwargs):
       """Raise a HTTPException for the given http_status_code. Attach any keyword
       arguments to the exception for later processing.
   
  @@ -36,6 +36,7 @@
       except HTTPException as err:
           if len(kwargs):
               err.data = kwargs
  +        err.exc = exc
           raise err
  
verification_info: {"golden_diff": "diff --git a/webargs/flaskparser.py b/webargs/flaskparser.py\n--- a/webargs/flaskparser.py\n+++ b/webargs/flaskparser.py\n@@ -25,7 +25,7 @@\n from webargs import core\n \n \n-def abort(http_status_code, **kwargs):\n+def abort(http_status_code, exc=None, **kwargs):\n     \"\"\"Raise a HTTPException for the given http_status_code. Attach any keyword\n     arguments to the exception for later processing.\n \n@@ -36,6 +36,7 @@\n     except HTTPException as err:\n         if len(kwargs):\n             err.data = kwargs\n+        err.exc = exc\n         raise err\n", "issue": "ValidationError causes HTTP/500 in Flask instead of HTTP/422 in Python 2\nHere are the symptoms: http://stackoverflow.com/questions/37321835/flask-error-handler-not-able-to-handle-assertion-error/39624893#39624893\n\nHere is the case where I personally hit the issue with Python 2.x and PyPy in Python 2.x mode only: https://travis-ci.org/frol/flask-restplus-server-example/builds/161685419\n\nAfter troubleshooting, I discovered that this issue stems from the `FlaskParser.handle_error` which calls `abort` function with `exc=` argument, which then [is set as \"data\"](https://github.com/sloria/webargs/blob/dev/webargs/flaskparser.py#L38) on the raised `HTTPException`, which in its turn is tried to be json-dumped and fails with `TypeError`, and now the new exception is in `sys.exc_info()`, which causes Flask to [assert here](https://github.com/pallets/flask/blob/01081dbe6cdfa3fc43d8e1fff708d4ed95e1be7e/flask/app.py#L1515).\n\nSearching git history for the introduced `exc=` in `FlaskParser`, I found it here: https://github.com/sloria/webargs/commit/6f8088c7c85c05d2891e81ee97d38f2ae801159d\n\nIs there a reason for `exc=` to be passed? How can we fix this?\n\nPython 3 seems to have changed something with `sys.exc_info()` behaviour since even after the `TypeError` caused by `json.dumps()`, `sys.exc_info()` still reports `HTTPException` while in Python 2 it returns `TypeError`.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Flask request argument parsing module.\n\nExample: ::\n\n    from flask import Flask\n\n    from webargs import fields\n    from webargs.flaskparser import use_args\n\n    app = Flask(__name__)\n\n    hello_args = {\n        'name': fields.Str(required=True)\n    }\n\n    @app.route('/')\n    @use_args(hello_args)\n    def index(args):\n        return 'Hello ' + args['name']\n\"\"\"\nimport flask\nfrom werkzeug.exceptions import HTTPException\n\nfrom webargs import core\n\n\ndef abort(http_status_code, **kwargs):\n    \"\"\"Raise a HTTPException for the given http_status_code. Attach any keyword\n    arguments to the exception for later processing.\n\n    From Flask-Restful. See NOTICE file for license information.\n    \"\"\"\n    try:\n        flask.abort(http_status_code)\n    except HTTPException as err:\n        if len(kwargs):\n            err.data = kwargs\n        raise err\n\n\ndef is_json_request(req):\n    return core.is_json(req.mimetype)\n\n\nclass FlaskParser(core.Parser):\n    \"\"\"Flask request argument parser.\"\"\"\n\n    __location_map__ = dict(\n        view_args='parse_view_args',\n        **core.Parser.__location_map__\n    )\n\n    def parse_view_args(self, req, name, field):\n        \"\"\"Pull a value from the request's ``view_args``.\"\"\"\n        return core.get_value(req.view_args, name, field)\n\n    def parse_json(self, req, name, field):\n        \"\"\"Pull a json value from the request.\"\"\"\n        # Pass force in order to handle vendor media types,\n        # e.g. applications/vnd.json+api\n        # this should be unnecessary in Flask 1.0\n        force = is_json_request(req)\n        # Fail silently so that the webargs parser can handle the error\n        if hasattr(req, 'get_json'):\n            # Flask >= 0.10.x\n            json_data = req.get_json(force=force, silent=True)\n        else:\n            # Flask <= 0.9.x\n            json_data = req.json\n        if json_data is None:\n            return core.missing\n        return core.get_value(json_data, name, field, allow_many_nested=True)\n\n    def parse_querystring(self, req, name, field):\n        \"\"\"Pull a querystring value from the request.\"\"\"\n        return core.get_value(req.args, name, field)\n\n    def parse_form(self, req, name, field):\n        \"\"\"Pull a form value from the request.\"\"\"\n        try:\n            return core.get_value(req.form, name, field)\n        except AttributeError:\n            pass\n        return core.missing\n\n    def parse_headers(self, req, name, field):\n        \"\"\"Pull a value from the header data.\"\"\"\n        return core.get_value(req.headers, name, field)\n\n    def parse_cookies(self, req, name, field):\n        \"\"\"Pull a value from the cookiejar.\"\"\"\n        return core.get_value(req.cookies, name, field)\n\n    def parse_files(self, req, name, field):\n        \"\"\"Pull a file from the request.\"\"\"\n        return core.get_value(req.files, name, field)\n\n    def handle_error(self, error):\n        \"\"\"Handles errors during parsing. Aborts the current HTTP request and\n        responds with a 422 error.\n        \"\"\"\n        status_code = getattr(error, 'status_code', self.DEFAULT_VALIDATION_STATUS)\n        abort(status_code, messages=error.messages, exc=error)\n\n    def get_default_request(self):\n        \"\"\"Override to use Flask's thread-local request objec by default\"\"\"\n        return flask.request\n\nparser = FlaskParser()\nuse_args = parser.use_args\nuse_kwargs = parser.use_kwargs\n", "path": "webargs/flaskparser.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Flask request argument parsing module.\n\nExample: ::\n\n    from flask import Flask\n\n    from webargs import fields\n    from webargs.flaskparser import use_args\n\n    app = Flask(__name__)\n\n    hello_args = {\n        'name': fields.Str(required=True)\n    }\n\n    @app.route('/')\n    @use_args(hello_args)\n    def index(args):\n        return 'Hello ' + args['name']\n\"\"\"\nimport flask\nfrom werkzeug.exceptions import HTTPException\n\nfrom webargs import core\n\n\ndef abort(http_status_code, exc=None, **kwargs):\n    \"\"\"Raise a HTTPException for the given http_status_code. Attach any keyword\n    arguments to the exception for later processing.\n\n    From Flask-Restful. See NOTICE file for license information.\n    \"\"\"\n    try:\n        flask.abort(http_status_code)\n    except HTTPException as err:\n        if len(kwargs):\n            err.data = kwargs\n        err.exc = exc\n        raise err\n\n\ndef is_json_request(req):\n    return core.is_json(req.mimetype)\n\n\nclass FlaskParser(core.Parser):\n    \"\"\"Flask request argument parser.\"\"\"\n\n    __location_map__ = dict(\n        view_args='parse_view_args',\n        **core.Parser.__location_map__\n    )\n\n    def parse_view_args(self, req, name, field):\n        \"\"\"Pull a value from the request's ``view_args``.\"\"\"\n        return core.get_value(req.view_args, name, field)\n\n    def parse_json(self, req, name, field):\n        \"\"\"Pull a json value from the request.\"\"\"\n        # Pass force in order to handle vendor media types,\n        # e.g. applications/vnd.json+api\n        # this should be unnecessary in Flask 1.0\n        force = is_json_request(req)\n        # Fail silently so that the webargs parser can handle the error\n        if hasattr(req, 'get_json'):\n            # Flask >= 0.10.x\n            json_data = req.get_json(force=force, silent=True)\n        else:\n            # Flask <= 0.9.x\n            json_data = req.json\n        if json_data is None:\n            return core.missing\n        return core.get_value(json_data, name, field, allow_many_nested=True)\n\n    def parse_querystring(self, req, name, field):\n        \"\"\"Pull a querystring value from the request.\"\"\"\n        return core.get_value(req.args, name, field)\n\n    def parse_form(self, req, name, field):\n        \"\"\"Pull a form value from the request.\"\"\"\n        try:\n            return core.get_value(req.form, name, field)\n        except AttributeError:\n            pass\n        return core.missing\n\n    def parse_headers(self, req, name, field):\n        \"\"\"Pull a value from the header data.\"\"\"\n        return core.get_value(req.headers, name, field)\n\n    def parse_cookies(self, req, name, field):\n        \"\"\"Pull a value from the cookiejar.\"\"\"\n        return core.get_value(req.cookies, name, field)\n\n    def parse_files(self, req, name, field):\n        \"\"\"Pull a file from the request.\"\"\"\n        return core.get_value(req.files, name, field)\n\n    def handle_error(self, error):\n        \"\"\"Handles errors during parsing. Aborts the current HTTP request and\n        responds with a 422 error.\n        \"\"\"\n        status_code = getattr(error, 'status_code', self.DEFAULT_VALIDATION_STATUS)\n        abort(status_code, messages=error.messages, exc=error)\n\n    def get_default_request(self):\n        \"\"\"Override to use Flask's thread-local request objec by default\"\"\"\n        return flask.request\n\nparser = FlaskParser()\nuse_args = parser.use_args\nuse_kwargs = parser.use_kwargs\n", "path": "webargs/flaskparser.py"}]}