Skip to content

Commit aa37c0a

Browse files
committedMar 30, 2025
Merge app and request contexts into a single context (pallets#5639)
1 parent f61172b commit aa37c0a

File tree

3 files changed

+138
-323
lines changed

3 files changed

+138
-323
lines changed
 

‎src/flask/app.py

+43-94
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@
2828

2929
from . import cli
3030
from . import typing as ft
31-
from .ctx import AppContext
32-
from .ctx import RequestContext
33-
from .globals import _cv_app
34-
from .globals import _cv_request
31+
from .ctx import ExecutionContext
32+
from .globals import _cv_execution
3533
from .globals import current_app
3634
from .globals import g
3735
from .globals import request
@@ -1057,7 +1055,7 @@ def url_for(
10571055
.. versionadded:: 2.2
10581056
Moved from ``flask.url_for``, which calls this method.
10591057
"""
1060-
req_ctx = _cv_request.get(None)
1058+
req_ctx = _cv_execution.get(None)
10611059

10621060
if req_ctx is not None:
10631061
url_adapter = req_ctx.url_adapter
@@ -1076,7 +1074,7 @@ def url_for(
10761074
if _external is None:
10771075
_external = _scheme is not None
10781076
else:
1079-
app_ctx = _cv_app.get(None)
1077+
app_ctx = _cv_execution.get(None)
10801078

10811079
# If called by helpers.url_for, an app context is active,
10821080
# use its url_adapter. Otherwise, app.url_for was called
@@ -1371,7 +1369,7 @@ def do_teardown_appcontext(
13711369
:data:`appcontext_tearing_down` signal is sent.
13721370
13731371
This is called by
1374-
:meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
1372+
:meth:`ExecutionContext.pop() <flask.ctx.ExecutionContext.pop>`.
13751373
13761374
.. versionadded:: 0.9
13771375
"""
@@ -1383,98 +1381,50 @@ def do_teardown_appcontext(
13831381

13841382
appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
13851383

1386-
def app_context(self) -> AppContext:
1387-
"""Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
1388-
block to push the context, which will make :data:`current_app`
1389-
point at this application.
1384+
def app_context(self) -> ExecutionContext:
1385+
"""Create an :class:`~flask.ctx.ExecutionContext`. This is typically
1386+
used with the :keyword:`with` statement to set up a context, but
1387+
the functions that do this can also be used directly.
13901388
1391-
An application context is automatically pushed by
1392-
:meth:`RequestContext.push() <flask.ctx.RequestContext.push>`
1393-
when handling a request, and when running a CLI command. Use
1394-
this to manually create a context outside of these situations.
1395-
1396-
::
1397-
1398-
with app.app_context():
1399-
init_db()
1400-
1401-
See :doc:`/appcontext`.
1402-
1403-
.. versionadded:: 0.9
1389+
.. versionchanged:: 3.0
1390+
The context is now created using the new unified execution context.
14041391
"""
1405-
return AppContext(self)
1406-
1407-
def request_context(self, environ: WSGIEnvironment) -> RequestContext:
1408-
"""Create a :class:`~flask.ctx.RequestContext` representing a
1409-
WSGI environment. Use a ``with`` block to push the context,
1410-
which will make :data:`request` point at this request.
1392+
return ExecutionContext(self)
14111393

1412-
See :doc:`/reqcontext`.
1394+
def request_context(self, environ: WSGIEnvironment) -> ExecutionContext:
1395+
"""Create a :class:`~flask.ctx.ExecutionContext` for a WSGI
1396+
environment. Use a :class:`~werkzeug.test.EnvironBuilder` to create
1397+
such an environment. See the :doc:`/testing` guide for more
1398+
information.
14131399
1414-
Typically you should not call this from your own code. A request
1415-
context is automatically pushed by the :meth:`wsgi_app` when
1416-
handling a request. Use :meth:`test_request_context` to create
1417-
an environment and context instead of this method.
1418-
1419-
:param environ: a WSGI environment
1420-
"""
1421-
return RequestContext(self, environ)
1422-
1423-
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext:
1424-
"""Create a :class:`~flask.ctx.RequestContext` for a WSGI
1425-
environment created from the given values. This is mostly useful
1426-
during testing, where you may want to run a function that uses
1427-
request data without dispatching a full request.
1428-
1429-
See :doc:`/reqcontext`.
1430-
1431-
Use a ``with`` block to push the context, which will make
1432-
:data:`request` point at the request for the created
1433-
environment. ::
1434-
1435-
with app.test_request_context(...):
1436-
generate_report()
1437-
1438-
When using the shell, it may be easier to push and pop the
1439-
context manually to avoid indentation. ::
1440-
1441-
ctx = app.test_request_context(...)
1442-
ctx.push()
1443-
...
1444-
ctx.pop()
1445-
1446-
Takes the same arguments as Werkzeug's
1447-
:class:`~werkzeug.test.EnvironBuilder`, with some defaults from
1448-
the application. See the linked Werkzeug docs for most of the
1449-
available arguments. Flask-specific behavior is listed here.
1450-
1451-
:param path: URL path being requested.
1452-
:param base_url: Base URL where the app is being served, which
1453-
``path`` is relative to. If not given, built from
1454-
:data:`PREFERRED_URL_SCHEME`, ``subdomain``,
1455-
:data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
1456-
:param subdomain: Subdomain name to append to
1457-
:data:`SERVER_NAME`.
1458-
:param url_scheme: Scheme to use instead of
1459-
:data:`PREFERRED_URL_SCHEME`.
1460-
:param data: The request body, either as a string or a dict of
1461-
form keys and values.
1462-
:param json: If given, this is serialized as JSON and passed as
1463-
``data``. Also defaults ``content_type`` to
1464-
``application/json``.
1465-
:param args: other positional arguments passed to
1466-
:class:`~werkzeug.test.EnvironBuilder`.
1467-
:param kwargs: other keyword arguments passed to
1468-
:class:`~werkzeug.test.EnvironBuilder`.
1400+
The request and app contexts are now combined into a single execution context.
14691401
"""
1470-
from .testing import EnvironBuilder
1402+
return ExecutionContext(self, environ=environ)
14711403

1472-
builder = EnvironBuilder(self, *args, **kwargs)
1404+
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> ExecutionContext:
1405+
"""Create a :class:`~flask.ctx.ExecutionContext` for a test request.
1406+
Use a :class:`~werkzeug.test.EnvironBuilder` to create such an
1407+
environment. See the :doc:`/testing` guide for more information.
14731408
1474-
try:
1475-
return self.request_context(builder.get_environ())
1476-
finally:
1477-
builder.close()
1409+
The request and app contexts are now combined into a single execution context.
1410+
"""
1411+
environ = {
1412+
"HTTP_HOST": "localhost",
1413+
"PATH_INFO": "/",
1414+
"REQUEST_METHOD": "GET",
1415+
"SERVER_NAME": "localhost",
1416+
"SERVER_PORT": "80",
1417+
"SERVER_PROTOCOL": "HTTP/1.1",
1418+
"wsgi.errors": sys.stderr,
1419+
"wsgi.input": sys.stdin,
1420+
"wsgi.multiprocess": False,
1421+
"wsgi.multithread": False,
1422+
"wsgi.run_once": False,
1423+
"wsgi.url_scheme": "http",
1424+
"wsgi.version": (1, 0),
1425+
}
1426+
environ.update(*args, **kwargs)
1427+
return self.request_context(environ)
14781428

14791429
def wsgi_app(
14801430
self, environ: WSGIEnvironment, start_response: StartResponse
@@ -1518,8 +1468,7 @@ def wsgi_app(
15181468
return response(environ, start_response)
15191469
finally:
15201470
if "werkzeug.debug.preserve_context" in environ:
1521-
environ["werkzeug.debug.preserve_context"](_cv_app.get())
1522-
environ["werkzeug.debug.preserve_context"](_cv_request.get())
1471+
environ["werkzeug.debug.preserve_context"](_cv_execution.get())
15231472

15241473
if error is not None and self.should_ignore_error(error):
15251474
error = None

0 commit comments

Comments
 (0)