Use when refactoring vercel-py sync and async modules to share transport-agnostic business logic with the iter_coroutine pattern, including base/runtime splits and shared HTTP request clients.
Instrucciones de origen · Vista previa de solo lectura
name
iter-coroutine
description
Use when refactoring vercel-py sync and async modules to share transport-agnostic business logic with the iter_coroutine pattern, including base/runtime splits and shared HTTP request clients.
Iter-Coroutine + Base/Runtime Migration
Refactor sync+async modules to eliminate duplication using the iter-coroutine
pattern. This skill covers the full migration workflow: identifying candidates,
structuring internal modules, writing transport-agnostic business logic, and
wiring up sync/async public APIs.
When to use
A module has parallel sync and async implementations with duplicated logic.
You're adding a new feature that needs both sync and async public APIs.
You're refactoring an existing feature to use the shared HTTP transport layer.
Core principles
Public API is stable. Same exported names, signatures, return types, and
behavior. Migrations are internal refactors, not API changes.
Internal core is async-first. All shared business logic lives in async
methods on a base class.
Sync entrypoints are thin wrappers over the async core via
iter_coroutine(...), used only when the wrapped coroutine is non-suspending
in sync mode.
HTTP goes through vercel._internal.http transports. Never construct
httpx.Client or httpx.AsyncClient directly in feature modules.
Architecture overview
Public API (sync wrappers + async functions)
│
│ iter_coroutine() one-shot bridge (sync path)
│ await (async path)
▼
Concrete Clients (SyncFooClient / AsyncFooClient)
│
│ inherit from
▼
BaseFooClient (all async methods — shared business logic)
│
│ uses
▼
RequestClient (transport-agnostic async API)
│
▼
Transport (SyncTransport or AsyncTransport)
│
▼
httpx.Client or httpx.AsyncClient
How iter_coroutine works
# src/vercel/_internal/iter_coroutine.pydefiter_coroutine(coro: Coroutine[None, None, _T]) -> _T:
"""Execute a non-suspending coroutine synchronously."""try:
coro.send(None)
except StopIteration as ex:
return ex.value
else:
raise RuntimeError(f"coroutine {coro!r} did not stop after one iteration!")
finally:
coro.close()
It drives a coroutine forward exactly one step. If the coroutine completes
without suspending, its return value is extracted from the StopIteration
exception. If it tries to yield/await, it raises RuntimeError.
Safety rules
Safe
Unsafe
Coroutines that only call sync code via async API
Coroutines that await real I/O
SyncTransport.send() (sync under async facade)
AsyncTransport.send() (real await)
Sync sleep, sync file I/O
asyncio.sleep, asyncio.create_task
inspect.isawaitable() guarded branches
Unguarded await on user callbacks
Step-by-step migration
1. Create the transport-agnostic request client
The request client wraps a BaseTransport and exposes an async API. The
transport implementation determines whether I/O is actually sync or async.