Why anyio?
https://github.com/encode/httpcore/issues/344
https://github.com/encode/httpcore/pull/169
https://github.com/encode/httpcore/pull/420
-----------------------
Updated at 2024-08-22
这些函数都可以直接从asynctor获取了: pip install asynctor
from asynctor.aio import gather, run_async, wait_for, start_tasks
-----------------------
1. gather
Example:
import anyio
async def gather(*coros):
results = [None] * len(coros)
async def runner(coro, i):
results[i] = await coro
async with anyio.create_task_group() as tg:
for i, coro in enumerate(coros):
tg.start_soon(runner, coro, i)
return results
async def foo(i):
print(i)
async def main():
await gather(*[foo(i) for i in range(5)])
if __name__ == "__main__":
anyio.run(main)
2. run_until_complete
asyncio:
import asyncio
from typing import Coroutine
from tortoise import Toirtoise
def run_async(coro: Coroutine) -> None:
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(coro)
finally:
loop.run_until_complete(Tortoise.close_connections())
--> anyio:
import anyio
from typing import Coroutine
from tortoise import Toirtoise
def run_async(coro: Coroutine) -> None:
async def runner():
try:
await coro
finally:
await Tortoise.close_connections()
anyio.run(runner)
3. wait_for
asyncio
import asyncio
try:
await asyncio.wait_for(do_sth(), 10)
except asyncio.TimeoutError:
pass
anyio
Cancellation and timeouts — AnyIO 4.0.0 documentation
from typing import Any, Coroutine, Union
import anyio
async def wait_for(coro: Coroutine, timeout: Union[int, float]) -> Any:
with anyio.fail_after(timeout):
return await coro
try:
await wait_for(do_sth, 10)
except TimeoutError:
pass
4. create_task
asyncio
import asyncio
class Runner:
def __init__(self):
self.value = 0
async def run_main(self):
while True:
await asyncio.sleep(0.1)
self.value += 1
runner = Runner()
@app.on_event('startup')
async def startup():
asyncio.create_task(runner.run_main())
@app.get('/')
def root():
return runner.value
anyio
async with: https://peps.python.org/pep-0492/#new-syntax
import anyio
from fastapi import FastAPI
app = FastAPI()
class Runner:
def __init__(self):
self.value = 0
async def run_main(self):
while True:
await anyio.sleep(0.1)
self.value += 1
runner = Runner()
def register(app):
tg = anyio.create_task_group()
scope = anyio.CancelScope(shield=True)
@app.on_event("startup")
async def startup():
await tg.__aenter__()
scope.__enter__()
tg.start_soon(runner.run_main)
@app.on_event("shutdown")
async def shutdown():
tg.cancel_scope.cancel()
scope.__exit__(None, None, None)
await tg.__aexit__(None, None, None)
register(app)
@app.get("/")
def root():
return runner.value
或anyio+lifespan
from contextlib import asynccontextmanager
import anyio
from fastapi import FastAPI
class Runner:
def __init__(self):
self.value = 0
async def run_main(self):
while True:
await anyio.sleep(0.1)
self.value += 1
runner = Runner()
@asynccontextmanager
async def lifespan(app: FastAPI):
async with anyio.create_task_group() as tg:
with anyio.CancelScope(shield=True):
tg.start_soon(runner.run_main)
yield
tg.cancel_scope.cancel()
app = FastAPI(lifespan=lifespan)
@app.get("/")
def root():
return runner.value
https://anyio.readthedocs.io/en/stable/cancellation.html#shielding
封装成asyncio风格的:
from contextlib import asynccontextmanager
from typing import Callable, Coroutine
import anyio
from fastapi import FastAPI
def be_afunc(coro: Coroutine) -> Callable:
async def do_await():
return await coro
return do_await
@asynccontextmanager
async def create_task(coro: Coroutine, *more: Coroutine):
async with anyio.create_task_group() as tg:
with anyio.CancelScope(shield=True):
tg.start_soon(be_afunc(coro))
for c in more:
tg.start_soon(be_afunc(c))
yield
tg.cancel_scope.cancel()
class Runner:
def __init__(self) -> None:
self.value = 0
async def run_main(self) -> None:
while True:
await anyio.sleep(0.1)
self.value += 1
runner = Runner()
@asynccontextmanager
async def lifespan(app: FastAPI):
async with create_task(runner.run_main()):
yield
app = FastAPI(lifespan=lifespan)
@app.get("/")
def root():
return runner.value