forked from orbit-oss/flask
Improve application context popping
Exceptions during teardown handling will no longer leave application contexts lingering around. This fixes #1767
This commit is contained in:
parent
87787b130b
commit
8482ce6b8c
3 changed files with 61 additions and 33 deletions
2
CHANGES
2
CHANGES
|
|
@ -77,6 +77,8 @@ Version 0.11
|
||||||
- ``send_from_directory`` now raises BadRequest if the filename is invalid on
|
- ``send_from_directory`` now raises BadRequest if the filename is invalid on
|
||||||
the server OS (pull request ``#1763``).
|
the server OS (pull request ``#1763``).
|
||||||
- Added the ``JSONIFY_MIMETYPE`` configuration variable (pull request ``#1728``).
|
- Added the ``JSONIFY_MIMETYPE`` configuration variable (pull request ``#1728``).
|
||||||
|
- Exceptions during teardown handling will no longer leave bad application
|
||||||
|
contexts lingering around.
|
||||||
|
|
||||||
Version 0.10.2
|
Version 0.10.2
|
||||||
--------------
|
--------------
|
||||||
|
|
|
||||||
70
flask/ctx.py
70
flask/ctx.py
|
|
@ -181,12 +181,14 @@ class AppContext(object):
|
||||||
|
|
||||||
def pop(self, exc=_sentinel):
|
def pop(self, exc=_sentinel):
|
||||||
"""Pops the app context."""
|
"""Pops the app context."""
|
||||||
self._refcnt -= 1
|
try:
|
||||||
if self._refcnt <= 0:
|
self._refcnt -= 1
|
||||||
if exc is _sentinel:
|
if self._refcnt <= 0:
|
||||||
exc = sys.exc_info()[1]
|
if exc is _sentinel:
|
||||||
self.app.do_teardown_appcontext(exc)
|
exc = sys.exc_info()[1]
|
||||||
rv = _app_ctx_stack.pop()
|
self.app.do_teardown_appcontext(exc)
|
||||||
|
finally:
|
||||||
|
rv = _app_ctx_stack.pop()
|
||||||
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
||||||
% (rv, self)
|
% (rv, self)
|
||||||
appcontext_popped.send(self.app)
|
appcontext_popped.send(self.app)
|
||||||
|
|
@ -341,38 +343,40 @@ class RequestContext(object):
|
||||||
"""
|
"""
|
||||||
app_ctx = self._implicit_app_ctx_stack.pop()
|
app_ctx = self._implicit_app_ctx_stack.pop()
|
||||||
|
|
||||||
clear_request = False
|
try:
|
||||||
if not self._implicit_app_ctx_stack:
|
clear_request = False
|
||||||
self.preserved = False
|
if not self._implicit_app_ctx_stack:
|
||||||
self._preserved_exc = None
|
self.preserved = False
|
||||||
if exc is _sentinel:
|
self._preserved_exc = None
|
||||||
exc = sys.exc_info()[1]
|
if exc is _sentinel:
|
||||||
self.app.do_teardown_request(exc)
|
exc = sys.exc_info()[1]
|
||||||
|
self.app.do_teardown_request(exc)
|
||||||
|
|
||||||
# If this interpreter supports clearing the exception information
|
# If this interpreter supports clearing the exception information
|
||||||
# we do that now. This will only go into effect on Python 2.x,
|
# we do that now. This will only go into effect on Python 2.x,
|
||||||
# on 3.x it disappears automatically at the end of the exception
|
# on 3.x it disappears automatically at the end of the exception
|
||||||
# stack.
|
# stack.
|
||||||
if hasattr(sys, 'exc_clear'):
|
if hasattr(sys, 'exc_clear'):
|
||||||
sys.exc_clear()
|
sys.exc_clear()
|
||||||
|
|
||||||
request_close = getattr(self.request, 'close', None)
|
request_close = getattr(self.request, 'close', None)
|
||||||
if request_close is not None:
|
if request_close is not None:
|
||||||
request_close()
|
request_close()
|
||||||
clear_request = True
|
clear_request = True
|
||||||
|
finally:
|
||||||
|
rv = _request_ctx_stack.pop()
|
||||||
|
|
||||||
rv = _request_ctx_stack.pop()
|
# get rid of circular dependencies at the end of the request
|
||||||
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
|
# so that we don't require the GC to be active.
|
||||||
% (rv, self)
|
if clear_request:
|
||||||
|
rv.request.environ['werkzeug.request'] = None
|
||||||
|
|
||||||
# get rid of circular dependencies at the end of the request
|
# Get rid of the app as well if necessary.
|
||||||
# so that we don't require the GC to be active.
|
if app_ctx is not None:
|
||||||
if clear_request:
|
app_ctx.pop(exc)
|
||||||
rv.request.environ['werkzeug.request'] = None
|
|
||||||
|
|
||||||
# Get rid of the app as well if necessary.
|
assert rv is self, 'Popped wrong request context. ' \
|
||||||
if app_ctx is not None:
|
'(%r instead of %r)' % (rv, self)
|
||||||
app_ctx.pop(exc)
|
|
||||||
|
|
||||||
def auto_pop(self, exc):
|
def auto_pop(self, exc):
|
||||||
if self.request.environ.get('flask._preserve_context') or \
|
if self.request.environ.get('flask._preserve_context') or \
|
||||||
|
|
|
||||||
|
|
@ -146,3 +146,25 @@ def test_context_refcounts():
|
||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
assert res.data == b''
|
assert res.data == b''
|
||||||
assert called == ['request', 'app']
|
assert called == ['request', 'app']
|
||||||
|
|
||||||
|
|
||||||
|
def test_clean_pop():
|
||||||
|
called = []
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
|
@app.teardown_request
|
||||||
|
def teardown_req(error=None):
|
||||||
|
1 / 0
|
||||||
|
|
||||||
|
@app.teardown_appcontext
|
||||||
|
def teardown_app(error=None):
|
||||||
|
called.append('TEARDOWN')
|
||||||
|
|
||||||
|
try:
|
||||||
|
with app.test_request_context():
|
||||||
|
called.append(flask.current_app.name)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
assert called == ['test_appctx', 'TEARDOWN']
|
||||||
|
assert not flask.current_app
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue