From a417e41d27cc14a0fe74dc49840b8921aeed70b6 Mon Sep 17 00:00:00 2001 From: Bijan Vakili Date: Fri, 7 Jul 2017 17:34:44 -0700 Subject: [PATCH 1/2] Update documentation and regression tests to clarify that Flask.teardown_appcontext() only receives unhandled exceptions --- flask/app.py | 6 +++-- tests/test_appctx.py | 53 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/flask/app.py b/flask/app.py index fb791f33..63faa5e2 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1484,8 +1484,10 @@ class Flask(_PackageBoundObject): Since a request context typically also manages an application context it would also be called when you pop a request context. - When a teardown function was called because of an exception it will - be passed an error object. + When a teardown function was called because of an unhandled exception + it will be passed an error object. Note that if a :meth:`errorhandler` + is registered, it will handle the exception and the teardown will not + receive it. The return values of teardown functions are ignored. diff --git a/tests/test_appctx.py b/tests/test_appctx.py index 7ef7b479..bf3474e9 100644 --- a/tests/test_appctx.py +++ b/tests/test_appctx.py @@ -81,7 +81,7 @@ def test_app_tearing_down_with_previous_exception(app): assert cleanup_stuff == [None] -def test_app_tearing_down_with_handled_exception(app): +def test_app_tearing_down_with_handled_exception_by_except_block(app): cleanup_stuff = [] @app.teardown_appcontext @@ -97,6 +97,57 @@ def test_app_tearing_down_with_handled_exception(app): assert cleanup_stuff == [None] +def test_app_tearing_down_with_handled_exception_by_app_handler(app): + cleanup_stuff = [] + + class AppConfig(): + PROPAGATE_EXCEPTIONS = True + app.config.from_object(AppConfig()) + + @app.teardown_appcontext + def cleanup(exception): + cleanup_stuff.append(exception) + + @app.route('/') + def index(): + raise Exception('dummy') + + @app.errorhandler(Exception) + def handler(f): + return flask.jsonify(str(f)) + + test_client = app.test_client() + with app.app_context(): + test_client.get('/') + + assert cleanup_stuff == [None] + + +def test_app_tearing_down_with_unhandled_exception(app): + cleanup_stuff = [] + + class AppConfig(): + PROPAGATE_EXCEPTIONS = True + app.config.from_object(AppConfig()) + + @app.teardown_appcontext + def cleanup(exception): + cleanup_stuff.append(exception) + + @app.route('/') + def index(): + raise Exception('dummy') + + test_client = app.test_client() + with pytest.raises(Exception): + with app.app_context(): + test_client.get('/') + + assert len(cleanup_stuff) == 1 + assert isinstance(cleanup_stuff[0], Exception) + assert str(cleanup_stuff[0]) == 'dummy' + + def test_app_ctx_globals_methods(app, app_ctx): # get assert flask.g.get('foo') is None From 9560f22bb65d7c06d10ea3dfe3d7a81a73f68af2 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 10 Jul 2017 07:10:47 -0700 Subject: [PATCH 2/2] clean up --- flask/app.py | 4 ++-- tests/test_appctx.py | 20 ++++++-------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/flask/app.py b/flask/app.py index 63faa5e2..9dfc3783 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1485,8 +1485,8 @@ class Flask(_PackageBoundObject): context it would also be called when you pop a request context. When a teardown function was called because of an unhandled exception - it will be passed an error object. Note that if a :meth:`errorhandler` - is registered, it will handle the exception and the teardown will not + it will be passed an error object. If an :meth:`errorhandler` is + registered, it will handle the exception and the teardown will not receive it. The return values of teardown functions are ignored. diff --git a/tests/test_appctx.py b/tests/test_appctx.py index bf3474e9..fc2f6b13 100644 --- a/tests/test_appctx.py +++ b/tests/test_appctx.py @@ -97,13 +97,10 @@ def test_app_tearing_down_with_handled_exception_by_except_block(app): assert cleanup_stuff == [None] -def test_app_tearing_down_with_handled_exception_by_app_handler(app): +def test_app_tearing_down_with_handled_exception_by_app_handler(app, client): + app.config['PROPAGATE_EXCEPTIONS'] = True cleanup_stuff = [] - class AppConfig(): - PROPAGATE_EXCEPTIONS = True - app.config.from_object(AppConfig()) - @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -116,20 +113,16 @@ def test_app_tearing_down_with_handled_exception_by_app_handler(app): def handler(f): return flask.jsonify(str(f)) - test_client = app.test_client() with app.app_context(): - test_client.get('/') + client.get('/') assert cleanup_stuff == [None] -def test_app_tearing_down_with_unhandled_exception(app): +def test_app_tearing_down_with_unhandled_exception(app, client): + app.config['PROPAGATE_EXCEPTIONS'] = True cleanup_stuff = [] - class AppConfig(): - PROPAGATE_EXCEPTIONS = True - app.config.from_object(AppConfig()) - @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -138,10 +131,9 @@ def test_app_tearing_down_with_unhandled_exception(app): def index(): raise Exception('dummy') - test_client = app.test_client() with pytest.raises(Exception): with app.app_context(): - test_client.get('/') + client.get('/') assert len(cleanup_stuff) == 1 assert isinstance(cleanup_stuff[0], Exception)