Gap
download_document_content (_async/clients/api/management/_document.py:215) returns response.content
after a non-streaming dispatch through self._session.request(...). httpx's send defaults to
stream=False and then awaits response.aread(), so the whole body is materialised in memory. No public
surface exposes streaming or the session, and no workaround is documented.
Proposed API
async def stream_document_content(
self, document_id: GlpiId, *, chunk_size: int = 65536
) -> AsyncIterator[bytes]: ...
Codegen cost: one token
Verified by running the real unasync over a streaming probe:
async with -> with FREE (the bare `async` token is stripped)
async for -> for FREE
AsyncIterator -> Iterator FREE (unasync's built-in table)
aiter_bytes -> aiter_bytes NOT REWRITTEN <-- the only rule needed
Three coordinated edits:
"aiter_bytes": "iter_bytes" in TOKEN_REPLACEMENTS (unasync_build.py:88-99)
"aiter_bytes" in _INTENTIONAL_RENAMES (testing/tests/test_unasync_codegen.py:44-55) in the same
commit -- exactly the pattern aclose/aread already follow. Without it the guard reports
('new.py', 5, 'aiter_bytes').
- A test-harness change that is easy to miss:
testing/tests/test_method_invocation.py:169 installs
its stub only at client._session.request, and httpx.Client.stream does not route through
Client.request. A public stream_document_content would fail
test_every_public_method_reaches_the_transport on both surfaces and attempt a real socket connect to
https://glpi.example.test. _install_stub needs a .stream stub -- hand-written for each surface
(a @contextmanager for sync, an @asynccontextmanager for async), since that file is not
unasync-generated.
Two further gotchas
ensure_response_status reads response.text (_http.py:281,284), which raises ResponseNotRead on an
unread stream. Call aread()/read() first -- aread is already a substitution key.
- Do not decorate the generator with
@retry: per
docs/superpowers/specs/2026-07-15-real-async-io-design.md:345-346, tenacity silently degrades to the
sync path on an async generator.
Scope note
aiter_text / aiter_lines / aiter_raw are equally un-rewritten. Only aiter_bytes is needed for this
API; anyone generalising to text or line streaming needs the rest.
Gap
download_document_content(_async/clients/api/management/_document.py:215) returnsresponse.contentafter a non-streaming dispatch through
self._session.request(...). httpx'ssenddefaults tostream=Falseand then awaitsresponse.aread(), so the whole body is materialised in memory. No publicsurface exposes streaming or the session, and no workaround is documented.
Proposed API
Codegen cost: one token
Verified by running the real unasync over a streaming probe:
Three coordinated edits:
"aiter_bytes": "iter_bytes"inTOKEN_REPLACEMENTS(unasync_build.py:88-99)"aiter_bytes"in_INTENTIONAL_RENAMES(testing/tests/test_unasync_codegen.py:44-55) in the samecommit -- exactly the pattern
aclose/areadalready follow. Without it the guard reports('new.py', 5, 'aiter_bytes').testing/tests/test_method_invocation.py:169installsits stub only at
client._session.request, andhttpx.Client.streamdoes not route throughClient.request. A publicstream_document_contentwould failtest_every_public_method_reaches_the_transporton both surfaces and attempt a real socket connect tohttps://glpi.example.test._install_stubneeds a.streamstub -- hand-written for each surface(a
@contextmanagerfor sync, an@asynccontextmanagerfor async), since that file is notunasync-generated.
Two further gotchas
ensure_response_statusreadsresponse.text(_http.py:281,284), which raisesResponseNotReadon anunread stream. Call
aread()/read()first --areadis already a substitution key.@retry: perdocs/superpowers/specs/2026-07-15-real-async-io-design.md:345-346, tenacity silently degrades to thesync path on an async generator.
Scope note
aiter_text/aiter_lines/aiter_raware equally un-rewritten. Onlyaiter_bytesis needed for thisAPI; anyone generalising to text or line streaming needs the rest.