fix teardown bug in FlaskClient

* Fixes pytest-dev/pytest-flask#42
This commit is contained in:
Fantix King 2019-04-16 17:29:17 -05:00 committed by David Lord
parent 754b729578
commit a71c167836
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
3 changed files with 29 additions and 6 deletions

View File

@ -61,6 +61,9 @@ Unreleased
- Blueprints have a ``cli`` Click group like ``app.cli``. CLI commands - Blueprints have a ``cli`` Click group like ``app.cli``. CLI commands
registered with a blueprint will be available as a group under the registered with a blueprint will be available as a group under the
``flask`` command. :issue:`1357`. ``flask`` command. :issue:`1357`.
- When using the test client as a context manager (``with client:``),
all preserved request contexts are popped when the block exits,
ensuring nested contexts are cleaned up correctly. :pr:`3157`
.. _#2935: https://github.com/pallets/flask/issues/2935 .. _#2935: https://github.com/pallets/flask/issues/2935
.. _#2957: https://github.com/pallets/flask/issues/2957 .. _#2957: https://github.com/pallets/flask/issues/2957

View File

@ -206,12 +206,17 @@ class FlaskClient(Client):
def __exit__(self, exc_type, exc_value, tb): def __exit__(self, exc_type, exc_value, tb):
self.preserve_context = False self.preserve_context = False
# on exit we want to clean up earlier. Normally the request context # Normally the request context is preserved until the next
# stays preserved until the next request in the same thread comes # request in the same thread comes. When the client exits we
# in. See RequestGlobals.push() for the general behavior. # want to clean up earlier. Pop request contexts until the stack
top = _request_ctx_stack.top # is empty or a non-preserved one is found.
if top is not None and top.preserved: while True:
top.pop() top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop()
else:
break
class FlaskCliRunner(CliRunner): class FlaskCliRunner(CliRunner):

View File

@ -411,3 +411,18 @@ def test_cli_custom_obj(app):
runner = app.test_cli_runner() runner = app.test_cli_runner()
runner.invoke(hello_command, obj=script_info) runner.invoke(hello_command, obj=script_info)
assert NS.called assert NS.called
def test_client_pop_all_preserved(app, req_ctx, client):
@app.route("/")
def index():
# stream_with_context pushes a third context, preserved by client
return flask.Response(flask.stream_with_context("hello"))
# req_ctx fixture pushed an initial context, not marked preserved
with client:
# request pushes a second request context, preserved by client
client.get("/")
# only req_ctx fixture should still be pushed
assert flask._request_ctx_stack.top is req_ctx