-
Notifications
You must be signed in to change notification settings - Fork 3
Reset contextvar tokens in resolved_dependencies #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The resolved_dependencies function was setting contextvars without capturing reset tokens, which left stale references after the context manager exited. This could cause issues in reentrant or sequential calls, like "stack is closed" errors when the AsyncExitStack was already closed but still referenced. Now we capture tokens from all contextvar.set() calls and reset them in finally blocks to restore prior state. This follows the same pattern suggested in the fastmcp PR review: jlowin/fastmcp#2318 Added tests that verify contextvars are properly isolated and cleaned up between task executions, including a test for reentrant calls to resolved_dependencies that would previously fail with stale context. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
|
Claude finished @chrisguidry's task —— View job Code Review Complete ✅SummaryThis PR correctly addresses a contextvar cleanup issue in the ✅ Strengths
🔍 Code QualityThe implementation is clean and follows the existing codebase patterns:
✅ Security ConsiderationsNo security concerns identified. The change actually improves security by preventing potential context leakage between tasks. ✅ PerformanceMinimal performance impact - only adds token capture/reset operations which are negligible compared to task execution overhead. 🎯 Bug Fix VerificationThe fix correctly addresses the "stack is closed" errors that could occur in reentrant scenarios. The test RecommendationAPPROVED ✅ - This is a solid fix that correctly implements contextvar cleanup. The pattern matches Python's recommended approach and the test coverage is thorough. Ready to merge. |
|
📚 Documentation has been built for this PR! You can download the documentation directly here: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #178 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 34 34
Lines 5107 5216 +109
Branches 264 265 +1
==========================================
+ Hits 5107 5216 +109
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Updated test file to achieve 100% branch coverage: - Used ellipsis (...) syntax instead of pass for dummy functions - Simplified contextvar cleanup assertion using pytest.raises - Added pragma: no branch for pytest.raises edge case - Hoisted asynccontextmanager import to top of file 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense to me as described! Although it does seem like there is a test failing still and I can't understand why 🤔
|
Oh actually that's unrelated and just a task ordering expectation... carry on! |
Moved assertions from inside async task functions to the main test body. This avoids coverage tracking differences in Python 3.13 when assertions are inside async functions that run in the worker. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Tasks don't execute in a guaranteed order with parallel workers, so changed the test to use a dictionary lookup by task name instead of assuming array order. This fixes the flaky test failure in CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The
resolved_dependenciesfunction was setting contextvars without capturing reset tokens, which left stale references after the context manager exited. This could cause issues in reentrant or sequential calls, like "stack is closed" errors when the AsyncExitStack was already closed but still referenced.Now we capture tokens from all
contextvar.set()calls and reset them in finally blocks to restore prior state. This follows the same pattern suggested in the fastmcp PR review: jlowin/fastmcp#2318Changes
resolved_dependenciesto capture reset tokens from all 5 contextvar setsTesting
Added comprehensive tests including:
test_contextvar_reset_on_reentrant_call- Demonstrates the stale contextvar issue (fails without fix, passes with fix)test_contextvar_not_leaked_to_caller- Verifies no leakage outside contexttest_contextvar_isolation_between_tasks- Confirms isolation between sequential taskstest_contextvar_cleanup_after_task- Ensures cleanup after task completiontest_async_exit_stack_cleanup- Verifies AsyncExitStack cleanupAll 272 tests pass with 100% coverage maintained in
src/docket/dependencies.py.🤖 Generated with Claude Code