Skip to content

feat(python): add awaitable flat-file terminals for event-loop-safe AsyncClient use#884

Merged
userFRM merged 1 commit into
mainfrom
fix/round9-python
Jun 17, 2026
Merged

feat(python): add awaitable flat-file terminals for event-loop-safe AsyncClient use#884
userFRM merged 1 commit into
mainfrom
fix/round9-python

Conversation

@userFRM

@userFRM userFRM commented Jun 17, 2026

Copy link
Copy Markdown
Owner

The flat-file namespace is reachable from AsyncClient through the unified proxy allowlist, so async_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 through AsyncClient, 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 guarantee AsyncClient.connect was added to provide, undone by the flat-file surface. There was no awaitable flat-file terminal anywhere on the async surface.

This adds awaitable *_async twins for every flat-file fetch on the namespace, plus Client.flatfile_to_path_async:

  • option_trade_quote_async, option_open_interest_async, option_eod_async
  • stock_trade_quote_async, stock_eod_async
  • request_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 *_async terminals already use, so other coroutines keep running while the day's data arrives. The synchronous methods keep their blocking behaviour for plain Client use, so you pick the suffix that matches your call site: sync flat_files.option_eod(date) on a Client, 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 .pyi stub declares the new methods with Awaitable[...] 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 _async member 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 _async method whose base is not an enrolled fetch still trips it. python3 scripts/check_binding_parity.py is clean.

Build, fmt, clippy, and the crate test suite all pass.

🤖 Generated with Claude Code

…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>
@userFRM userFRM merged commit 10a7631 into main Jun 17, 2026
22 checks passed
@userFRM userFRM deleted the fix/round9-python branch June 17, 2026 20:47
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.

2 participants