GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||
GeezerVison is showing github.com — feat: share HTTP connection pool across SDK instances; refactor polling by jrvb-rl · Pull Request #797 · runloopai/api-client-python Conversation
Contributor
Summary
Test plan
🤖 Generated with Claude Code
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions- Add hasattr guard to AsyncAPIClient.close() matching sync version - Track per-instance _closed flag so is_closed() works correctly for shared pool clients (underlying transport stays open for others) - Add shared_http_pool param to copy()/with_options() to match __init__ - Add 19 tests covering sharing, refcounting, copy propagation Co-Authored-By: Claude Opus 4.6 <[email protected]>
james-rl
requested changes
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently there is some weird behavior in python that does not work the way that other languages do. This will cause issues for the async client (but not the sync client): import asyncio from runloop_api_client import AsyncRunloop async def run_once(): client = AsyncRunloop(bearer_token="token") await client.devboxes.retrieve("devbox-id") # imagine this client is not closed, or cleanup does not fully await close asyncio.run(run_once()) # creates/uses shared async pool on loop A asyncio.run(run_once()) # reuses same global AsyncClient on loop B
Python wants you to scope-manage the async pool instead of having one consolidated async pool. I find this extremely counterintuitive but it sorta makes sense given how the async client binds to the transport layer:
The suggestion is to add an extra bit of loop awareness so that requests don't accidentally tread on each other. Something a bit like: _async_pools: weakref.WeakKeyDictionary[asyncio.AbstractEventLoop, PoolState] loop = asyncio.get_running_loop() pool = _async_pools.get(loop) if pool is None or pool.client.is_closed: pool = PoolState(client=AsyncHttpxClientWrapper(...), refcount=0) _async_pools[loop] = pool The garbage collector should mop up resources, but this does also imply a need to change the close to ensure
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactionsCo-Authored-By: Claude Opus 4.6 <[email protected]> Address review feedback: async shared pool is now keyed by the running event loop using weakref.WeakKeyDictionary, so clients in different asyncio.run() calls get separate pools. close() properly awaits aclose() on the last release. __del__ uses a sync-only release path. Async tests converted to async def so they run inside an event loop and can exercise per-loop pool sharing correctly. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Contributor
Author
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. Fixes three bugs in the shared HTTP pool: (1) async clients created without a running event loop leaked connections, (2) __del__ could drop clients without closing them, (3) sharing httpx.Client shared cookie jars across SDK instances. Now shares only the transport (connection pool) via refcounted wrappers, giving each SDK instance its own httpx client with isolated mutable state. Co-Authored-By: Claude Opus 4.6 <[email protected]>
james-rl
approved these changes
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks good to me. One potential edge case for user-supplied http clients in comments. I also saw this changed: I think this is intentional, but since it was 100 previously I just want to call it out in case it was a temporary change.
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
Comment on lines
+286
to
+287
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think there is an edge case where a passed in The previous version had this:
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
Contributor
Author
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. After close(), Python can reuse the freed memory address for the next transport, making id() values match across separate asyncio.run() calls. Keep the first transport reference alive and use `is not` instead. Co-Authored-By: Claude Opus 4.6 <[email protected]> Uh oh!
There was an error while loading. Please reload this page.
Merged
2 of 4 tasks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ReviewersUh oh!
There was an error while loading. Please reload this page. AssigneesNo one assignedLabels
None yet
ProjectsMilestoneNo milestoneDevelopmentSuccessfully merging this pull request may close these issues. Uh oh!
There was an error while loading. Please reload this page. 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. | ||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |