diff --git a/docs/api.rst b/docs/api.rst index 217473bc..67772a77 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -311,56 +311,28 @@ Useful Internals .. autoclass:: flask.ctx.RequestContext :members: -.. data:: _request_ctx_stack +.. data:: flask.globals.request_ctx - The internal :class:`~werkzeug.local.LocalStack` that holds - :class:`~flask.ctx.RequestContext` instances. Typically, the - :data:`request` and :data:`session` proxies should be accessed - instead of the stack. It may be useful to access the stack in - extension code. + The current :class:`~flask.ctx.RequestContext`. If a request context + is not active, accessing attributes on this proxy will raise a + ``RuntimeError``. - The following attributes are always present on each layer of the - stack: - - `app` - the active Flask application. - - `url_adapter` - the URL adapter that was used to match the request. - - `request` - the current request object. - - `session` - the active session object. - - `g` - an object with all the attributes of the :data:`flask.g` object. - - `flashes` - an internal cache for the flashed messages. - - Example usage:: - - from flask import _request_ctx_stack - - def get_session(): - ctx = _request_ctx_stack.top - if ctx is not None: - return ctx.session + This is an internal object that is essential to how Flask handles + requests. Accessing this should not be needed in most cases. Most + likely you want :data:`request` and :data:`session` instead. .. autoclass:: flask.ctx.AppContext :members: -.. data:: _app_ctx_stack +.. data:: flask.globals.app_ctx - The internal :class:`~werkzeug.local.LocalStack` that holds - :class:`~flask.ctx.AppContext` instances. Typically, the - :data:`current_app` and :data:`g` proxies should be accessed instead - of the stack. Extensions can access the contexts on the stack as a - namespace to store data. + The current :class:`~flask.ctx.AppContext`. If an app context is not + active, accessing attributes on this proxy will raise a + ``RuntimeError``. - .. versionadded:: 0.9 + This is an internal object that is essential to how Flask handles + requests. Accessing this should not be needed in most cases. Most + likely you want :data:`current_app` and :data:`g` instead. .. autoclass:: flask.blueprints.BlueprintSetupState :members: diff --git a/docs/appcontext.rst b/docs/appcontext.rst index b214f254..a4ae3861 100644 --- a/docs/appcontext.rst +++ b/docs/appcontext.rst @@ -136,14 +136,6 @@ local from ``get_db()``:: Accessing ``db`` will call ``get_db`` internally, in the same way that :data:`current_app` works. ----- - -If you're writing an extension, :data:`g` should be reserved for user -code. You may store internal data on the context itself, but be sure to -use a sufficiently unique name. The current context is accessed with -:data:`_app_ctx_stack.top <_app_ctx_stack>`. For more information see -:doc:`/extensiondev`. - Events and Signals ------------------ diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 25ced187..95745119 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -187,12 +187,6 @@ when the application context ends. If it should only be valid during a request, or would not be used in the CLI outside a reqeust, use :meth:`~flask.Flask.teardown_request`. -An older technique for storing context data was to store it on -``_app_ctx_stack.top`` or ``_request_ctx_stack.top``. However, this just -moves the same namespace collision problem elsewhere (although less -likely) and modifies objects that are very internal to Flask's -operations. Prefer storing data under a unique name in ``g``. - Views and Models ---------------- diff --git a/docs/patterns/sqlite3.rst b/docs/patterns/sqlite3.rst index 12336fb1..5932589f 100644 --- a/docs/patterns/sqlite3.rst +++ b/docs/patterns/sqlite3.rst @@ -30,10 +30,6 @@ or create an application context itself. At that point the ``get_db`` function can be used to get the current database connection. Whenever the context is destroyed the database connection will be terminated. -Note: if you use Flask 0.9 or older you need to use -``flask._app_ctx_stack.top`` instead of ``g`` as the :data:`flask.g` -object was bound to the request and not application context. - Example:: @app.route('/') diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst index 96e17e3b..2b109770 100644 --- a/docs/reqcontext.rst +++ b/docs/reqcontext.rst @@ -37,12 +37,14 @@ context, which also pushes an :doc:`app context `. When the request ends it pops the request context then the application context. The context is unique to each thread (or other worker type). -:data:`request` cannot be passed to another thread, the other thread -will have a different context stack and will not know about the request -the parent thread was pointing to. +:data:`request` cannot be passed to another thread, the other thread has +a different context space and will not know about the request the parent +thread was pointing to. -Context locals are implemented in Werkzeug. See :doc:`werkzeug:local` -for more information on how this works internally. +Context locals are implemented using Python's :mod:`contextvars` and +Werkzeug's :class:`~werkzeug.local.LocalProxy`. Python manages the +lifetime of context vars automatically, and local proxy wraps that +low-level interface to make the data easier to work with. Manually Push a Context @@ -87,10 +89,9 @@ How the Context Works The :meth:`Flask.wsgi_app` method is called to handle each request. It manages the contexts during the request. Internally, the request and -application contexts work as stacks, :data:`_request_ctx_stack` and -:data:`_app_ctx_stack`. When contexts are pushed onto the stack, the +application contexts work like stacks. When contexts are pushed, the proxies that depend on them are available and point at information from -the top context on the stack. +the top item. When the request starts, a :class:`~ctx.RequestContext` is created and pushed, which creates and pushes an :class:`~ctx.AppContext` first if @@ -99,10 +100,10 @@ these contexts are pushed, the :data:`current_app`, :data:`g`, :data:`request`, and :data:`session` proxies are available to the original thread handling the request. -Because the contexts are stacks, other contexts may be pushed to change -the proxies during a request. While this is not a common pattern, it -can be used in advanced applications to, for example, do internal -redirects or chain different applications together. +Other contexts may be pushed to change the proxies during a request. +While this is not a common pattern, it can be used in advanced +applications to, for example, do internal redirects or chain different +applications together. After the request is dispatched and a response is generated and sent, the request context is popped, which then pops the application context. diff --git a/src/flask/app.py b/src/flask/app.py index 084429aa..19d77351 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -1284,29 +1284,30 @@ class Flask(Scaffold): @setupmethod def teardown_appcontext(self, f: T_teardown) -> T_teardown: - """Registers a function to be called when the application context - ends. These functions are typically also called when the request - context is popped. + """Registers a function to be called when the application + context is popped. The application context is typically popped + after the request context for each request, at the end of CLI + commands, or after a manually pushed context ends. - Example:: + .. code-block:: python - ctx = app.app_context() - ctx.push() - ... - ctx.pop() + with app.app_context(): + ... - When ``ctx.pop()`` is executed in the above example, the teardown - functions are called just before the app context moves from the - stack of active contexts. This becomes relevant if you are using - such constructs in tests. + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the app context is + made inactive. Since a request context typically also manages an + application context it would also be called when you pop a + request context. - Since a request context typically also manages an application - context it would also be called when you pop a request context. + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. - When a teardown function was called because of an unhandled exception - it will be passed an error object. If an :meth:`errorhandler` is - registered, it will handle the exception and the teardown will not - receive it. + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. The return values of teardown functions are ignored. diff --git a/src/flask/ctx.py b/src/flask/ctx.py index 62242e80..1ea2719b 100644 --- a/src/flask/ctx.py +++ b/src/flask/ctx.py @@ -227,12 +227,10 @@ def has_app_context() -> bool: class AppContext: - """The application context binds an application object implicitly - to the current thread or greenlet, similar to how the - :class:`RequestContext` binds request information. The application - context is also implicitly created if a request context is created - but the application is not on top of the individual application - context. + """The app context contains application-specific information. An app + context is created and pushed at the beginning of each request if + one is not already active. An app context is also pushed when + running CLI commands. """ def __init__(self, app: "Flask") -> None: @@ -278,10 +276,10 @@ class AppContext: class RequestContext: - """The request context contains all request relevant information. It is - created at the beginning of the request and pushed to the - `_request_ctx_stack` and removed at the end of it. It will create the - URL adapter and request object for the WSGI environment provided. + """The request context contains per-request information. The Flask + app creates and pushes it at the beginning of the request, then pops + it at the end of the request. It will create the URL adapter and + request object for the WSGI environment provided. Do not attempt to use this class directly, instead use :meth:`~flask.Flask.test_request_context` and diff --git a/src/flask/scaffold.py b/src/flask/scaffold.py index 7f099f40..1f6882ac 100644 --- a/src/flask/scaffold.py +++ b/src/flask/scaffold.py @@ -574,30 +574,27 @@ class Scaffold: @setupmethod def teardown_request(self, f: T_teardown) -> T_teardown: - """Register a function to be run at the end of each request, - regardless of whether there was an exception or not. These functions - are executed when the request context is popped, even if not an - actual request was performed. + """Register a function to be called when the request context is + popped. Typically this happens at the end of each request, but + contexts may be pushed manually as well during testing. - Example:: + .. code-block:: python - ctx = app.test_request_context() - ctx.push() - ... - ctx.pop() + with app.test_request_context(): + ... - When ``ctx.pop()`` is executed in the above example, the teardown - functions are called just before the request context moves from the - stack of active contexts. This becomes relevant if you are using - such constructs in tests. + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the request context is + made inactive. - Teardown functions must avoid raising exceptions. If - they execute code that might fail they - will have to surround the execution of that code with try/except - statements and log any errors. + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. - When a teardown function was called because of an exception it will - be passed an error object. + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. The return values of teardown functions are ignored. """ diff --git a/src/flask/testing.py b/src/flask/testing.py index e188439b..bf84f848 100644 --- a/src/flask/testing.py +++ b/src/flask/testing.py @@ -94,11 +94,10 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder): class FlaskClient(Client): - """Works like a regular Werkzeug test client but has some knowledge about - how Flask works to defer the cleanup of the request context stack to the - end of a ``with`` body when used in a ``with`` statement. For general - information about how to use this class refer to - :class:`werkzeug.test.Client`. + """Works like a regular Werkzeug test client but has knowledge about + Flask's contexts to defer the cleanup of the request context until + the end of a ``with`` block. For general information about how to + use this class refer to :class:`werkzeug.test.Client`. .. versionchanged:: 0.12 `app.test_client()` includes preset default environment, which can be