-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathfastapi_app.py
More file actions
108 lines (73 loc) · 3.04 KB
/
Copy pathfastapi_app.py
File metadata and controls
108 lines (73 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""Runnable FastAPI + WebSocket example served on hvloop.
Two ways to run it (both use hvloop as the event loop):
1. Directly:
python examples/fastapi_app.py
``main()`` calls ``uvicorn.run(app, loop="hvloop:new_event_loop")``.
uvicorn (>= 0.36) treats any ``"module:callable"`` string as an event-loop
*factory*, so it builds its loop by calling ``hvloop.new_event_loop()``.
2. Via the uvicorn CLI, passing the same loop factory:
uvicorn examples.fastapi_app:app --loop hvloop:new_event_loop
NOTE: with uvicorn >= 0.36, ``hvloop.install()`` + ``--loop asyncio`` does NOT
work: uvicorn maps ``asyncio`` directly to ``asyncio.SelectorEventLoop`` and
never consults the (deprecated) asyncio event-loop policy, so that recipe
silently runs on stock asyncio. Use ``--loop hvloop:new_event_loop`` (or
``hvloop.install()`` + ``--loop none``). See README "Running uvicorn + FastAPI
on hvloop".
Then try it out::
curl http://127.0.0.1:8000/
curl http://127.0.0.1:8000/loopinfo # proves which loop class is running
curl http://127.0.0.1:8000/items/42?q=hi
# WebSocket echo (needs the `websockets` package):
python -m websockets ws://127.0.0.1:8000/ws
Requires: ``pip install hvloop fastapi uvicorn`` (plus ``websockets`` for /ws).
"""
from __future__ import annotations
import asyncio
import contextlib
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import StreamingResponse
@contextlib.asynccontextmanager
async def lifespan(_app: FastAPI):
print("hvloop example: startup")
yield
print("hvloop example: shutdown")
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def root():
return {"hello": "hvloop", "server": "uvicorn+fastapi"}
@app.get("/loopinfo")
async def loopinfo():
# Sanity probe: shows the event-loop class actually serving this request.
# Expect "hvloop.Loop" when wired correctly (see module docstring).
cls = type(asyncio.get_running_loop())
return {"loop_class": f"{cls.__module__}.{cls.__qualname__}"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.get("/sync")
def sync_endpoint():
# Plain ``def`` endpoints run in Starlette's thread pool.
return {"mode": "sync"}
@app.get("/stream")
async def stream():
async def gen():
for i in range(5):
yield f"chunk {i}\n".encode()
return StreamingResponse(gen(), media_type="text/plain")
@app.websocket("/ws")
async def ws_echo(ws: WebSocket):
await ws.accept()
try:
while True:
message = await ws.receive_text()
await ws.send_text(f"echo: {message}")
except WebSocketDisconnect:
pass
def main() -> None:
import uvicorn
# loop="hvloop:new_event_loop" hands uvicorn the hvloop loop factory
# directly -- the only wiring that works with uvicorn >= 0.36 without
# touching the deprecated asyncio policy system.
uvicorn.run(app, host="127.0.0.1", port=8000, loop="hvloop:new_event_loop")
if __name__ == "__main__":
main()