Python(fix): sift client lifecycle improvements#595
Merged
Conversation
144ed4f to
702d577
Compare
solidiquis
previously approved these changes
May 29, 2026
702d577 to
a16c005
Compare
Contributor
|
Python docs preview: https://sift-stack.github.io/sift/python/pr-595/ Deployed from |
solidiquis
approved these changes
May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
GrpcClient.close_sync()/close()now clear_channels_asyncand_stubs_async_mapafter teardown so the channel objects are released promptly instead of lingering past interpreter exit.close_sync()is now idempotent (guards against double-close), skips work when the loop is already stopped, and uses a.result(timeout=...)so an unresponsive channel can't block forever at exit.TestGrpcClientClosecovering the ref clearing and the idempotent second-call paths.Why
The pytest plugin (and any long-lived sync caller) was leaving
grpc_wait_for_shutdown_with_timeout() timed outat process exit, plus a silent ~2s stall while gRPC waited.The gRPC C-core releases a channel's resources only when the Python channel object is destroyed, not when
close()returns. Under pytest the session-scoped client kept channels alive in its maps until interpreter finalization, so the C-core's own exit-time shutdown raced live channels and timed out. Clearing the maps insideclose()drops the refs, refcounting destroys the channels, and the C-core's later C-level atexit then finds nothing to wait on.The idempotency guard fixes a separate latent hang: closing explicitly (or via the context manager) and then having
atexitclose again submitted a coroutine to the already-stopped loop, where.result()blocked forever.