fastapi-reloader 1.2.1__py2.py3-none-any.whl → 1.3.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_reloader/core.py +5 -0
- fastapi_reloader/patcher.py +36 -19
- fastapi_reloader/runtime.js +18 -14
- {fastapi_reloader-1.2.1.dist-info → fastapi_reloader-1.3.1.dist-info}/METADATA +1 -1
- fastapi_reloader-1.3.1.dist-info/RECORD +8 -0
- fastapi_reloader-1.2.1.dist-info/RECORD +0 -8
- {fastapi_reloader-1.2.1.dist-info → fastapi_reloader-1.3.1.dist-info}/WHEEL +0 -0
- {fastapi_reloader-1.2.1.dist-info → fastapi_reloader-1.3.1.dist-info}/entry_points.txt +0 -0
fastapi_reloader/core.py
CHANGED
@@ -33,6 +33,11 @@ async def heartbeat():
|
|
33
33
|
return Response(status_code=200)
|
34
34
|
|
35
35
|
|
36
|
+
@reload_router.get("/poller.js")
|
37
|
+
async def get_poller_js():
|
38
|
+
return Response(get_js(), media_type="application/javascript")
|
39
|
+
|
40
|
+
|
36
41
|
@reload_router.get("/{key:int}")
|
37
42
|
async def simple_refresh_trigger(key: int):
|
38
43
|
async def event_generator():
|
fastapi_reloader/patcher.py
CHANGED
@@ -1,24 +1,49 @@
|
|
1
1
|
from collections.abc import Awaitable, Callable
|
2
2
|
from contextlib import asynccontextmanager
|
3
|
+
from copy import copy
|
3
4
|
from math import inf
|
4
5
|
from typing import TypeGuard
|
5
6
|
|
6
7
|
from asgi_lifespan import LifespanManager
|
7
8
|
from fastapi import FastAPI, Request, Response
|
8
9
|
from fastapi.responses import StreamingResponse
|
10
|
+
from starlette.applications import Starlette
|
9
11
|
from starlette.middleware import Middleware
|
10
12
|
from starlette.middleware.base import BaseHTTPMiddleware
|
11
13
|
from starlette.types import ASGIApp
|
12
14
|
|
13
|
-
from .core import
|
15
|
+
from .core import reload_router
|
14
16
|
|
15
17
|
|
16
18
|
def is_streaming_response(response: Response) -> TypeGuard[StreamingResponse]:
|
17
|
-
# In fact, it may not be a
|
19
|
+
# In fact, it may not be a Starlette's StreamingResponse, but an internal one with the same interface
|
18
20
|
return hasattr(response, "body_iterator")
|
19
21
|
|
20
22
|
|
21
|
-
def
|
23
|
+
async def _injection_http_middleware(request: Request, call_next: Callable[[Request], Awaitable[Response]]):
|
24
|
+
res = await call_next(request)
|
25
|
+
|
26
|
+
if request.method != "GET" or "html" not in (res.headers.get("content-type", "")) or res.headers.get("content-encoding", "identity") != "identity":
|
27
|
+
return res
|
28
|
+
|
29
|
+
async def response():
|
30
|
+
if is_streaming_response(res):
|
31
|
+
async for chunk in res.body_iterator:
|
32
|
+
yield chunk
|
33
|
+
else:
|
34
|
+
yield res.body
|
35
|
+
|
36
|
+
yield b'\n\n <script src="/---fastapi-reloader---/poller.js"></script>'
|
37
|
+
|
38
|
+
headers = {k: v for k, v in res.headers.items() if k.lower() not in {"content-length", "transfer-encoding"}}
|
39
|
+
|
40
|
+
return StreamingResponse(response(), res.status_code, headers, res.media_type)
|
41
|
+
|
42
|
+
|
43
|
+
injection_middleware = Middleware(BaseHTTPMiddleware, dispatch=_injection_http_middleware)
|
44
|
+
|
45
|
+
|
46
|
+
def _reloader_middleware(app: ASGIApp):
|
22
47
|
@asynccontextmanager
|
23
48
|
async def lifespan(_):
|
24
49
|
async with LifespanManager(app, inf, inf):
|
@@ -28,24 +53,16 @@ def patch_for_auto_reloading(app: ASGIApp):
|
|
28
53
|
new_app.include_router(reload_router)
|
29
54
|
new_app.mount("/", app)
|
30
55
|
|
31
|
-
|
32
|
-
res = await call_next(request)
|
33
|
-
|
34
|
-
if request.method != "GET" or "html" not in (res.headers.get("content-type", "")):
|
35
|
-
return res
|
36
|
-
|
37
|
-
async def response():
|
38
|
-
if is_streaming_response(res):
|
39
|
-
async for chunk in res.body_iterator:
|
40
|
-
yield chunk
|
41
|
-
else:
|
42
|
-
yield res.body
|
43
|
-
yield f"\n\n <script> {get_js()} </script>".encode()
|
56
|
+
return new_app
|
44
57
|
|
45
|
-
headers = {k: v for k, v in res.headers.items() if k.lower() not in {"content-length", "content-encoding", "transfer-encoding"}}
|
46
58
|
|
47
|
-
|
59
|
+
def patch_for_auto_reloading(app: ASGIApp):
|
60
|
+
if isinstance(app, Starlette): # both FastAPI and Starlette have user_middleware attribute
|
61
|
+
new_app = copy(app)
|
62
|
+
new_app.user_middleware = [*app.user_middleware, injection_middleware] # before compression middlewares
|
63
|
+
return _reloader_middleware(new_app)
|
48
64
|
|
49
|
-
new_app
|
65
|
+
new_app = _reloader_middleware(app)
|
66
|
+
new_app.user_middleware.append(injection_middleware) # the last middleware is the first one to be called
|
50
67
|
|
51
68
|
return new_app
|
fastapi_reloader/runtime.js
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
|
1
|
+
async function poll() {
|
2
|
+
while (true) {
|
3
|
+
try {
|
4
|
+
const res = await fetch("/---fastapi-reloader---", { method: "HEAD" });
|
5
|
+
if (res.ok) {
|
6
|
+
break;
|
7
|
+
} else if (res.status !== 502) {
|
8
|
+
return;
|
9
|
+
}
|
10
|
+
} catch (error) {}
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
async function main() {
|
2
15
|
const response = await fetch("/---fastapi-reloader---/0");
|
3
16
|
const reader = response.body.getReader();
|
4
17
|
const decoder = new TextDecoder();
|
@@ -10,20 +23,11 @@
|
|
10
23
|
if (value) {
|
11
24
|
const chunk = decoder.decode(value, { stream: true });
|
12
25
|
if (chunk.includes("1")) {
|
13
|
-
|
14
|
-
try {
|
15
|
-
const res = await fetch("/---fastapi-reloader---", {
|
16
|
-
method: "HEAD",
|
17
|
-
});
|
18
|
-
if (res.ok) {
|
19
|
-
break;
|
20
|
-
} else if (res.status !== 502) {
|
21
|
-
return;
|
22
|
-
}
|
23
|
-
} catch (error) {}
|
24
|
-
}
|
26
|
+
await poll();
|
25
27
|
location.reload();
|
26
28
|
}
|
27
29
|
}
|
28
30
|
}
|
29
|
-
}
|
31
|
+
}
|
32
|
+
|
33
|
+
main();
|
@@ -0,0 +1,8 @@
|
|
1
|
+
fastapi_reloader-1.3.1.dist-info/METADATA,sha256=xKLO2Z0ZWciUVgTReqnzDklb611n6zA5PEthLU59ODA,2270
|
2
|
+
fastapi_reloader-1.3.1.dist-info/WHEEL,sha256=pz1FfwQ2kf9tI4G8U2ObRTKdvsTSmrreuBTtdnO8pJw,94
|
3
|
+
fastapi_reloader-1.3.1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
+
fastapi_reloader/__init__.py,sha256=VE5m-0M92Weme8COqLs0LwIx5g6FHhqyOLGAgY6Zkzs,145
|
5
|
+
fastapi_reloader/core.py,sha256=eAyPhBZaFiKepy5NcU966adAJwqFTpHlK_uXn1cJQU8,1676
|
6
|
+
fastapi_reloader/patcher.py,sha256=nwn9sJW8KsXUr7dSPClWuPCanP453ymazfetVtuPaDA,2436
|
7
|
+
fastapi_reloader/runtime.js,sha256=pUPeMnMuKGHhBTudp6bFuNTTyZhGSvrvvWy_lWVN8nA,713
|
8
|
+
fastapi_reloader-1.3.1.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
fastapi_reloader-1.2.1.dist-info/METADATA,sha256=6zo0A2A0mHvuK2wTyuv6VUMBf795MrYB3d4rL6ROwmE,2270
|
2
|
-
fastapi_reloader-1.2.1.dist-info/WHEEL,sha256=pz1FfwQ2kf9tI4G8U2ObRTKdvsTSmrreuBTtdnO8pJw,94
|
3
|
-
fastapi_reloader-1.2.1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
-
fastapi_reloader/__init__.py,sha256=VE5m-0M92Weme8COqLs0LwIx5g6FHhqyOLGAgY6Zkzs,145
|
5
|
-
fastapi_reloader/core.py,sha256=lrfBJoavB2sDzkvN5UyWV91Xm69iLCuigg7pzKKVGfc,1547
|
6
|
-
fastapi_reloader/patcher.py,sha256=pwomRoZUbWth2SA5L7zEQsAy4qBuepnOYyOqCnqig2Y,1909
|
7
|
-
fastapi_reloader/runtime.js,sha256=H9SCpyGpBUmfvcdlzpU4RXnIBCYSQwIqXZ7jd1bdtSo,743
|
8
|
-
fastapi_reloader-1.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|