feat(python): add awaitable flat-file terminals for event-loop-safe AsyncClient use#884
Merged
Conversation
…syncClient use The flat-file namespace was reachable from AsyncClient through the unified proxy allowlist, but its only terminals were synchronous. A flat-file pull is a full-day blob download of seconds, so reaching option_trade_quote(date) and friends through AsyncClient.flat_files drove the whole download to completion on the event loop thread and stalled every other coroutine for its duration, defeating the non-blocking guarantee AsyncClient is built to provide. There was no awaitable flat-file terminal on the async surface. Add awaitable *_async twins for every flat-file fetch on FlatFilesNamespace (option_trade_quote_async, option_open_interest_async, option_eod_async, stock_trade_quote_async, stock_eod_async, request_async) plus Client.flatfile_to_path_async, each resolving the download off the event loop through the same awaitable bridge the historical *_async terminals use. The synchronous methods keep their blocking behaviour for plain Client use, so await flat_files.option_eod_async(date) inside a coroutine yields the same FlatFileRowList without holding the loop. The bridge never holds the interpreter lock across the download; the row materialisation runs only after the data has arrived. Declare the new awaitable methods in the .pyi stub with Awaitable return types, verified against the built module with stubtest. The flat-file reverse-orphan parity scan now recognises an _async member as the awaitable twin of its enrolled sync fetch by matching the base name, mirroring how the cross-binding async surface is tracked elsewhere; an _async method whose base is not an enrolled fetch still trips the gate, so the check is not weakened. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
The flat-file namespace is reachable from
AsyncClientthrough the unified proxy allowlist, soasync_client.flat_files.option_trade_quote(date)resolves. The catch is that the namespace only had synchronous terminals. A flat-file pull is a full-day blob download that runs for seconds, and the sync method drives it to completion on the calling thread. Reached throughAsyncClient, that calling thread is the event loop thread, so the download stalled every other coroutine for its whole duration. That is exactly the non-blocking guaranteeAsyncClient.connectwas added to provide, undone by the flat-file surface. There was no awaitable flat-file terminal anywhere on the async surface.This adds awaitable
*_asynctwins for every flat-file fetch on the namespace, plusClient.flatfile_to_path_async:option_trade_quote_async,option_open_interest_async,option_eod_asyncstock_trade_quote_async,stock_eod_asyncrequest_async(the generic string-keyed dispatcher)flatfile_to_path_async(blob straight to disk, yields the path)Each resolves the download off the event loop through the same awaitable bridge the historical
*_asyncterminals already use, so other coroutines keep running while the day's data arrives. The synchronous methods keep their blocking behaviour for plainClientuse, so you pick the suffix that matches your call site: syncflat_files.option_eod(date)on aClient,await flat_files.option_eod_async(date)inside a coroutine. The bridge never holds the interpreter lock across the download; the row build runs only after the data is in hand, identical to the historical async path.The
.pyistub declares the new methods withAwaitable[...]return types, checked against the built module with stubtest (no discrepancies on the flat-file surface). The flat-file reverse-orphan parity scan now treats an_asyncmember as the awaitable twin of its enrolled sync fetch by matching the base name, the same way the cross-binding async surface is tracked elsewhere. This does not weaken the gate: an_asyncmethod whose base is not an enrolled fetch still trips it.python3 scripts/check_binding_parity.pyis clean.Build, fmt, clippy, and the crate test suite all pass.
🤖 Generated with Claude Code