Context Teardown and Cleanup
When you open a database connection or a file handle during a request, you need a guaranteed way to close it, even if the request fails with an unhandled exception. If you don't, your application will eventually leak resources, leading to "Too many open files" or exhausted connection pools. Flask solves this by triggering a teardown process whenever an application context is removed.
Registering Teardown Functions
To ensure a resource is cleaned up, register a function using the @app.teardown_appcontext decorator. Flask calls these functions at the end of every request or CLI command.
from flask import Flask, g
app = Flask(__name__)
@app.teardown_appcontext
def close_db_connection(exception=None):
db = g.pop("db", None)
if db is not None:
db.close()
The function receives one argument: the exception that caused the context to end, or None if the request finished successfully.
The Teardown Lifecycle
The teardown process is managed by the AppContext.pop() method in src/flask/ctx.py. This method is called automatically when a with app.app_context(): block exits or when the WSGI server finishes processing a request.
When pop() is invoked, Flask executes cleanup in a specific sequence:
- Request Teardown: If the context contains request data, Flask first calls functions registered with
@app.teardown_requestviaself.app.do_teardown_request(self, exc). - Request Closing: The request object itself is closed via
self._request.close(). - App Context Teardown: Flask calls functions registered with
@app.teardown_appcontextviaself.app.do_teardown_appcontext(self, exc). - Signal Dispatch: The
appcontext_poppedsignal is sent.
Reverse Execution Order
Teardown functions are executed in the reverse order of their registration. If you register function A and then function B, Flask will execute B then A. This ensures that if B depends on a resource set up by A, that resource is still available when B runs.
Internally, Flask.do_teardown_appcontext in src/flask/app.py handles this:
for func in reversed(self.teardown_appcontext_funcs):
with collect_errors:
self.ensure_sync(func)(exc)
Robust Cleanup with _CollectErrors
Flask ensures that a failure in one teardown function does not prevent others from running. It uses a utility class called _CollectErrors (found in src/flask/helpers.py) to wrap every teardown call.
If a teardown function raises an exception, _CollectErrors catches it and stores it. After all functions have been attempted, Flask raises the collected errors. On Python 3.11 and newer, these are raised as an ExceptionGroup; on older versions, the first error is re-raised.
Nested Contexts and Push Counts
In complex scenarios like testing or streaming, you might push the same AppContext multiple times. Flask tracks this using a _push_count attribute in the AppContext class.
Cleanup logic—including teardown functions and signals—only executes when the _push_count reaches zero. This means that if you manually push a context twice, you must pop it twice before any resources are actually released.
ctx = app.app_context()
ctx.push() # _push_count = 1
ctx.push() # _push_count = 2
ctx.pop() # _push_count = 1, no teardown yet
ctx.pop() # _push_count = 0, teardown functions run now
Teardown Signals
Flask provides two signals for monitoring the teardown process:
appcontext_tearing_down: Sent at the beginning of the app context teardown process, before the registered functions are called. It includes theexcargument.appcontext_popped: Sent at the very end ofAppContext.pop(), after all cleanup is complete and the context has been removed from the internal stack.
These signals are useful for extensions that need to coordinate their own cleanup with the Flask lifecycle without requiring the user to register a specific teardown function.