Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore handled exception when popping the context #1393

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Patches and Suggestions
- Kenneth Reitz
- Keyan Pishdadian
- Marian Sigler
- Martijn Pieters
- Matt Campell
- Matthew Frazier
- Michael van Tellingen
Expand Down
11 changes: 7 additions & 4 deletions flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
# a lock used for logger initialization
_logger_lock = Lock()

# a singleton sentinel value for parameter defaults
_sentinel = object()


def _make_timedelta(value):
if not isinstance(value, timedelta):
Expand Down Expand Up @@ -1774,7 +1777,7 @@ def process_response(self, response):
self.save_session(ctx.session, response)
return response

def do_teardown_request(self, exc=None):
def do_teardown_request(self, exc=_sentinel):
"""Called after the actual request dispatching and will
call every as :meth:`teardown_request` decorated function. This is
not actually called by the :class:`Flask` object itself but is always
Expand All @@ -1785,7 +1788,7 @@ def do_teardown_request(self, exc=None):
Added the `exc` argument. Previously this was always using the
current exception information.
"""
if exc is None:
if exc is _sentinel:
exc = sys.exc_info()[1]
funcs = reversed(self.teardown_request_funcs.get(None, ()))
bp = _request_ctx_stack.top.request.blueprint
Expand All @@ -1795,14 +1798,14 @@ def do_teardown_request(self, exc=None):
func(exc)
request_tearing_down.send(self, exc=exc)

def do_teardown_appcontext(self, exc=None):
def do_teardown_appcontext(self, exc=_sentinel):
"""Called when an application context is popped. This works pretty
much the same as :meth:`do_teardown_request` but for the application
context.

.. versionadded:: 0.9
"""
if exc is None:
if exc is _sentinel:
exc = sys.exc_info()[1]
for func in reversed(self.teardown_appcontext_funcs):
func(exc)
Expand Down
12 changes: 8 additions & 4 deletions flask/ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
from ._compat import BROKEN_PYPY_CTXMGR_EXIT, reraise


# a singleton sentinel value for parameter defaults
_sentinel = object()


class _AppCtxGlobals(object):
"""A plain object."""

Expand Down Expand Up @@ -168,11 +172,11 @@ def push(self):
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)

def pop(self, exc=None):
def pop(self, exc=_sentinel):
"""Pops the app context."""
self._refcnt -= 1
if self._refcnt <= 0:
if exc is None:
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_appcontext(exc)
rv = _app_ctx_stack.pop()
Expand Down Expand Up @@ -320,7 +324,7 @@ def push(self):
if self.session is None:
self.session = self.app.make_null_session()

def pop(self, exc=None):
def pop(self, exc=_sentinel):
"""Pops the request context and unbinds it by doing that. This will
also trigger the execution of functions registered by the
:meth:`~flask.Flask.teardown_request` decorator.
Expand All @@ -334,7 +338,7 @@ def pop(self, exc=None):
if not self._implicit_app_ctx_stack:
self.preserved = False
self._preserved_exc = None
if exc is None:
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)

Expand Down
15 changes: 15 additions & 0 deletions tests/test_appctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ def cleanup(exception):

assert cleanup_stuff == [None]

def test_app_tearing_down_with_handled_exception():
cleanup_stuff = []
app = flask.Flask(__name__)
@app.teardown_appcontext
def cleanup(exception):
cleanup_stuff.append(exception)

with app.app_context():
try:
raise Exception('dummy')
except Exception:
pass

assert cleanup_stuff == [None]

def test_custom_app_ctx_globals_class():
class CustomRequestGlobals(object):
def __init__(self):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_reqctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ def end_of_request(exception):
assert buffer == []
assert buffer == [None]

def test_teardown_with_handled_exception():
buffer = []
app = flask.Flask(__name__)
@app.teardown_request
def end_of_request(exception):
buffer.append(exception)

with app.test_request_context():
assert buffer == []
try:
raise Exception('dummy')
except Exception:
pass
assert buffer == [None]

def test_proper_test_request_context():
app = flask.Flask(__name__)
app.config.update(
Expand Down