From 8569dfee6136bfda4a5fa2297aed3d82700bb1a5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 29 Nov 2010 08:57:38 +0100 Subject: [PATCH] Added a PROPAGATE_EXCEPTIONS flag --- CHANGES | 4 ++++ docs/config.rst | 8 ++++++++ flask/app.py | 15 ++++++++++++++- tests/flask_tests.py | 29 ++++++++++++++++++++++++++++- 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 1a452f40..7d9f9d25 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,10 @@ Release date to be announced, codename to be selected 1.0 the old behaviour will continue to work but issue dependency warnings. - fixed a problem for Flask to run on jython. +- added a `PROPAGATE_EXCEPTIONS` configuration variable that can be + used to flip the setting of exception propagation which previously + was linked to `DEBUG` alone and is now linked to either `DEBUG` or + `TESTING`. Version 0.6.1 ------------- diff --git a/docs/config.rst b/docs/config.rst index 1c2648a5..de74aa2b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -54,6 +54,11 @@ The following configuration values are used internally by Flask: =============================== ========================================= ``DEBUG`` enable/disable debug mode ``TESTING`` enable/disable testing mode +``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the + propagation of exceptions. If not set or + explicitly set to `None` this is + implicitly true if either `TESTING` or + `DEBUG` is true. ``SECRET_KEY`` the secret key ``SESSION_COOKIE_NAME`` the name of the session cookie ``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as @@ -96,6 +101,9 @@ The following configuration values are used internally by Flask: .. versionadded:: 0.6 ``MAX_CONTENT_LENGTH`` +.. versionadded:: 0.7 + ``PROPAGATE_EXCEPTIONS`` + Configuring from Files ---------------------- diff --git a/flask/app.py b/flask/app.py index f2ec45e9..312291e1 100644 --- a/flask/app.py +++ b/flask/app.py @@ -189,6 +189,7 @@ class Flask(_PackageBoundObject): default_config = ImmutableDict({ 'DEBUG': False, 'TESTING': False, + 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': None, 'SESSION_COOKIE_NAME': 'session', 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), @@ -303,6 +304,18 @@ class Flask(_PackageBoundObject): self.jinja_env = self.create_jinja_environment() self.init_jinja_globals() + @property + def propagate_exceptions(self): + """Returns the value of the `PROPAGATE_EXCEPTIONS` configuration + value in case it's set, otherwise a sensible default is returned. + + .. versionadded:: 0.7 + """ + rv = self.config['PROPAGATE_EXCEPTIONS'] + if rv is not None: + return rv + return self.testing or self.debug + @property def logger(self): """A :class:`logging.Logger` object for this application. The @@ -682,7 +695,7 @@ class Flask(_PackageBoundObject): """ got_request_exception.send(self, exception=e) handler = self.error_handlers.get(500) - if self.debug: + if self.propagate_exceptions: raise self.logger.exception('Exception on %s [%s]' % ( request.path, diff --git a/tests/flask_tests.py b/tests/flask_tests.py index dddabf21..c1cb95c2 100644 --- a/tests/flask_tests.py +++ b/tests/flask_tests.py @@ -15,6 +15,7 @@ import re import sys import flask import unittest +from threading import Thread from logging import StreamHandler from contextlib import contextmanager from datetime import datetime @@ -547,7 +548,6 @@ class BasicFunctionalityTestCase(unittest.TestCase): "No ValueError exception should have been raised \"%s\"" % e ) - def test_test_app_proper_environ(self): app = flask.Flask(__name__) app.config.update( @@ -619,6 +619,33 @@ class BasicFunctionalityTestCase(unittest.TestCase): "No ValueError exception should have been raised \"%s\"" % e ) + def test_exception_propagation(self): + def apprunner(configkey): + app = flask.Flask(__name__) + @app.route('/') + def index(): + 1/0 + c = app.test_client() + if config_key is not None: + app.config[config_key] = True + try: + resp = c.get('/') + except Exception: + pass + else: + self.fail('expected exception') + else: + assert c.get('/').status_code == 500 + + # we have to run this test in an isolated thread because if the + # debug flag is set to true and an exception happens the context is + # not torn down. This causes other tests that run after this fail + # when they expect no exception on the stack. + for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None: + t = Thread(target=apprunner, args=(config_key,)) + t.start() + t.join() + class JSONTestCase(unittest.TestCase):