-
Notifications
You must be signed in to change notification settings - Fork 329
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6092e34
commit d1b0e30
Showing
7 changed files
with
126 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import asyncio | ||
import os | ||
import signal | ||
import typing | ||
from contextlib import asynccontextmanager | ||
|
||
from fastapi import FastAPI | ||
from fastapi.responses import StreamingResponse | ||
from starlette import types | ||
|
||
|
||
def dev_fastapi_app(reload_path: str = '/api/__dev__/reload', **fastapi_kwargs) -> FastAPI: | ||
dev_reload = DevReload(fastapi_kwargs.pop('lifespan', None)) | ||
|
||
app = FastAPI(lifespan=dev_reload.lifespan) | ||
app.get(reload_path, include_in_schema=False)(dev_reload.dev_reload_endpoints) | ||
return app | ||
|
||
|
||
class DevReload: | ||
def __init__(self, default_lifespan: types.Lifespan[FastAPI] | None): | ||
self.default_lifespan = default_lifespan | ||
self.stop = asyncio.Event() | ||
|
||
@asynccontextmanager | ||
async def lifespan(self, app: FastAPI): | ||
signal.signal(signal.SIGTERM, self._on_signal) | ||
if self.default_lifespan: | ||
async with self.default_lifespan(app): | ||
yield | ||
else: | ||
yield | ||
|
||
async def dev_reload_endpoints(self): | ||
return StreamingResponse(self.ping()) | ||
|
||
def _on_signal(self, *_args: typing.Any): | ||
# print('setting stop', _args) | ||
self.stop.set() | ||
|
||
async def ping(self): | ||
# print('connected', os.getpid()) | ||
yield b'.' | ||
while True: | ||
try: | ||
await asyncio.wait_for(self.stop.wait(), timeout=2) | ||
except asyncio.TimeoutError: | ||
yield b'.' | ||
else: | ||
yield b'%d' % os.getpid() | ||
break |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { createContext, FC, ReactNode, useEffect, useState } from 'react' | ||
|
||
export const ReloadContext = createContext<number>(0) | ||
let devConnected = false | ||
|
||
export const DevReloadProvider: FC<{ children: ReactNode; enabled: boolean }> = ({ children, enabled }) => { | ||
const [value, setValue] = useState<number>(0) | ||
|
||
useEffect(() => { | ||
let listening = true | ||
async function listen() { | ||
let failCount = 0 | ||
while (true) { | ||
const response = await fetch('/api/__dev__/reload') | ||
// await like this means we wait for the entire response to be received | ||
const text = await response.text() | ||
const value = parseInt(text.replace(/\./g, '')) || 0 | ||
if (response.ok) { | ||
failCount = 0 | ||
} else { | ||
failCount++ | ||
} | ||
// wait long enough for the server to be back online | ||
await sleep(500) | ||
if (!listening || failCount >= 4) { | ||
return value | ||
} | ||
console.log('dev reload...') | ||
setValue(value) | ||
} | ||
} | ||
|
||
if (enabled && !devConnected) { | ||
devConnected = true | ||
listening = true | ||
listen().then((value) => { | ||
console.debug('dev reload disconnected.') | ||
setValue(value) | ||
}) | ||
return () => { | ||
listening = false | ||
devConnected = false | ||
} | ||
} | ||
}, [enabled]) | ||
|
||
return <ReloadContext.Provider value={value}>{children}</ReloadContext.Provider> | ||
} | ||
|
||
async function sleep(ms: number) { | ||
return new Promise((resolve) => setTimeout(resolve, ms)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters