Async¶
AsyncEnumerable[T] is the port of System.Linq.Async (IAsyncEnumerable<T>).
import asyncio
from pydantic_linq import AsyncEnumerable
async def fetch_users(): # any async iterable: an async generator, a DB cursor, ...
for i in range(5):
await asyncio.sleep(0)
yield i
async def score(user_id: int) -> int:
await asyncio.sleep(0)
return user_id * 10
async def main():
top = await (
AsyncEnumerable(fetch_users())
.where(lambda u: u > 0) # sync predicate
.select(score) # async selector
.order_by_descending(lambda s: s)
.take(2)
.to_list()
)
print(top) # [40, 30]
asyncio.run(main())
Rules¶
- Sources. Pass any
AsyncIterableor plainIterable. From a sync sequence you can also call.to_async_enumerable(). - Sync or async callables. Every selector, predicate, key and result selector can be a plain function or return an awaitable. That covers both the System.Linq.Async
*Awaitoverloads (SelectAwait,WhereAwait, ...) and the .NET 10AsyncEnumerableoverloads, with one method each. - Terminal operators are awaited.
await q.to_list(),await q.first(),await q.count(), and so on. The names drop the C#Asyncsuffix (ToListAsyncbecomesto_list). - Sequence operators are deferred, as in the sync version. Iterate them with
async for. - Grouping.
group_byyieldsAsyncGrouping, anAsyncEnumerablewith a.key.await to_lookup(...)returns the syncLookup, likeToLookupAsync.
One-shot sources
An async generator can only be iterated once, as in Python itself. To run two queries over it, create a new generator for each. Collections such as lists can be enumerated any number of times.