changeset 350769ccdfa5 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=350769ccdfa5
description:
Pass app and request to wsgi error handlers
issue8548
review271931002
diffstat:
CHANGELOG | 1 +
trytond/tests/test_wsgi.py | 118 +++++++++++++++++++++++++++++++++++++++++++++
trytond/wsgi.py | 6 +-
3 files changed, 124 insertions(+), 1 deletions(-)
diffs (153 lines):
diff -r 38d1b3a57920 -r 350769ccdfa5 CHANGELOG
--- a/CHANGELOG Mon Sep 02 18:21:41 2019 +0200
+++ b/CHANGELOG Mon Sep 02 18:25:12 2019 +0200
@@ -1,3 +1,4 @@
+* Pass app and request to wsgi error handlers
* Add lazy_gettext
* Add format_timedelta to Report
* Add partial TO_CHAR support for date and datetime on SQLite
diff -r 38d1b3a57920 -r 350769ccdfa5 trytond/tests/test_wsgi.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/tests/test_wsgi.py Mon Sep 02 18:25:12 2019 +0200
@@ -0,0 +1,118 @@
+# This file is part of Tryton. The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+import unittest
+from unittest.mock import Mock, sentinel
+
+from werkzeug.wrappers import Response
+from werkzeug.test import Client
+from trytond.wsgi import TrytondWSGI
+from trytond.exceptions import TrytonException
+
+
+class WSGIAppTestCase(unittest.TestCase):
+ 'Test WSGI Application'
+
+ class TestException(TrytonException):
+ pass
+
+ def test_one_error_handler_called(self):
+ """Test an error handler is called
+ with the app, the request and the exception."""
+ app = TrytondWSGI()
+ spy = Mock()
+
+ @app.error_handler
+ def _handler(*args, **kwargs):
+ spy(*args, **kwargs)
+
+ exception = self.TestException('foo')
+
+ @app.route('/willfail')
+ def _route(request):
+ sentinel.request = request
+ raise exception
+
+ client = Client(app)
+ (response, status, headers) = client.get('/willfail')
+
+ spy.assert_called_once_with(app, sentinel.request, exception)
+
+ def test_many_error_handlers_called(self):
+ "Test many error handlers are called"
+ app = TrytondWSGI()
+ spy1 = Mock()
+ spy2 = Mock()
+
+ @app.error_handler
+ def _handler1(*args, **kwargs):
+ spy1(*args, **kwargs)
+
+ @app.error_handler
+ def _handler2(*args, **kwargs):
+ spy2(*args, **kwargs)
+
+ exception = self.TestException('foo')
+
+ @app.route('/willfail')
+ def _route(request):
+ sentinel.request = request
+ raise exception
+
+ client = Client(app)
+ (response, status, headers) = client.get('/willfail')
+
+ spy1.assert_called_once_with(app, sentinel.request, exception)
+ spy2.assert_called_once_with(app, sentinel.request, exception)
+
+ def test_class_error_handler_called(self):
+ "Test class error handlers can be used"
+ app = TrytondWSGI()
+ spy = Mock()
+
+ class ErrorHandler():
+ def __call__(self, *args, **kwargs):
+ spy(*args, **kwargs)
+
+ app.error_handler(ErrorHandler())
+
+ exception = self.TestException('foo')
+
+ @app.route('/willfail')
+ def _route(request):
+ sentinel.request = request
+ raise exception
+
+ client = Client(app)
+ (response, status, headers) = client.get('/willfail')
+
+ spy.assert_called_once_with(app, sentinel.request, exception)
+
+ def test_error_handlers_last_response(self):
+ "Test last handler response is used"
+ app = TrytondWSGI()
+
+ @app.error_handler
+ def _handler1(*args, **kwargs):
+ return Response(b'bar')
+
+ @app.error_handler
+ def _handler2(*args, **kwargs):
+ return Response(b'baz', status=418)
+
+ @app.route('/willfail')
+ def _route(request):
+ raise self.TestException('foo')
+
+ client = Client(app)
+ (response, status, headers) = client.get('/willfail')
+
+ self.assertEqual(next(response), b'baz')
+ self.assertEqual(status, "418 I'M A TEAPOT")
+
+
+def suite():
+ suite_ = unittest.TestSuite()
+ suite_.addTests(
+ unittest.TestLoader().loadTestsFromTestCase(WSGIAppTestCase))
+ return suite_
diff -r 38d1b3a57920 -r 350769ccdfa5 trytond/wsgi.py
--- a/trytond/wsgi.py Mon Sep 02 18:21:41 2019 +0200
+++ b/trytond/wsgi.py Mon Sep 02 18:25:12 2019 +0200
@@ -65,6 +65,10 @@
return func
return decorator
+ def error_handler(self, handler):
+ self.error_handlers.append(handler)
+ return handler
+
@wrapt.decorator
def auth_required(self, wrapped, instance, args, kwargs):
request = args[0]
@@ -111,7 +115,7 @@
e.__format_traceback__ = tb_s
response = e
for error_handler in self.error_handlers:
- rv = error_handler(e)
+ rv = error_handler(self, request, e)
if isinstance(rv, Response):
response = rv
return response