Skip to content

fix: make the request container usable from every request hook - #42

Merged
lesnik512 merged 1 commit into
mainfrom
fix/request-container-for-all-hooks
Oct 17, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
fix/request-container-for-all-hooks

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Closes #41.

The request container no longer depends on hook registration order.

Changes

  • inject builds the request container on first use inside a request. before_request still builds it too, through the same get-or-create path, so it never replaces a container an earlier hook already built.
  • The container on g is tied to the flask.Request it was built for. A second request in the same app context gets its own container. A later lookup in the same request reuses the existing one, even if it is closed, so nothing is rebuilt after the close.
  • Outside a request, inject uses whatever container is on g and raises the same RuntimeError as before when there is none. An app context without a request (CLI, app.app_context()) never gets one built.
  • setup_di moves the close to index 0 of app.teardown_appcontext_funcs. Flask.do_teardown_appcontext runs that list in reverse, so the close runs after every other handler, whether it was registered before or after setup_di. Calling setup_di again keeps one close at the front.
  • Teardown only closes a container that is already on g. It never builds one.

Why keep the before_request creation

On Flask 3.0 and 3.1, RequestContext.pop resets the request before it pops the app context, so teardown_appcontext handlers run without a request and can't build a container. If creation were lazy only, a teardown_appcontext handler that injects would fail on any request where nothing used DI earlier. Building eagerly in before_request covers that case, and the lazy path covers hooks that run before it: early before_request hooks, and teardown_request after a before_request hook short-circuits.

Flask API used

app.teardown_appcontext_funcs is a documented attribute on Flask. The rest is flask.has_request_context, flask.current_app, flask.g and the existing flask.request._get_current_object().

Streaming

On Flask 3.0, stream_with_context keeps the context open for the whole stream, so the generator uses the live container. Since Flask 3.1.2 the context is torn down before the body is iterated and pushed again, so the generator sees the closed container and gets ContainerClosedError. That already happened before this change. The test accepts either outcome and checks that no second container is built.

@lesnik512
lesnik512 merged commit b2b78a8 into main Oct 17, 2026
12 checks passed
@lesnik512
lesnik512 deleted the fix/request-container-for-all-hooks branch October 17, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Request hooks registered before setup_di can't use the request container

1 participant