Skip to content

Commit 429275b

Browse files
authored
require a magic response for dev reload server to reconnect (#115)
1 parent 4274a8d commit 429275b

File tree

3 files changed

+34
-8
lines changed

3 files changed

+34
-8
lines changed

src/npm-fastui/src/dev.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { FC, useContext, useEffect } from 'react'
33
import { sleep } from './tools'
44
import { ErrorContext } from './hooks/error'
55
import { fireLoadEvent } from './events'
6+
import { ConfigContext } from './hooks/config'
67

78
let devConnected = false
89

@@ -20,6 +21,7 @@ export const DevReload: FC<{ enabled?: boolean }> = ({ enabled }) => {
2021

2122
const DevReloadActive = () => {
2223
const { setError } = useContext(ErrorContext)
24+
const { rootUrl } = useContext(ConfigContext)
2325

2426
useEffect(() => {
2527
let listening = true
@@ -34,21 +36,22 @@ const DevReloadActive = () => {
3436
if (!listening || failCount >= 5) {
3537
return count
3638
}
37-
const response = await fetch('/api/__dev__/reload')
39+
const response = await fetch(rootUrl + '/__dev__/reload')
3840
count++
3941
console.debug(`dev reload connected ${count}...`)
4042
// if the response is okay, and we previously failed, clear error
4143
if (response.ok && failCount > 0) {
4244
setError(null)
45+
} else if (response.status === 404) {
46+
console.log('dev reload endpoint not found, disabling dev reload')
47+
return count
4348
}
4449
// await like this means we wait for the entire response to be received
4550
const text = await response.text()
46-
if (response.status === 404) {
47-
console.log('dev reload endpoint not found, disabling dev reload')
48-
return count
49-
} else if (response.ok) {
51+
if (response.ok && text.startsWith('fastui-dev-reload')) {
5052
failCount = 0
51-
const value = parseInt(text.replace(/\./g, '')) || 0
53+
const valueMatch = text.match(/(\d+)$/)
54+
const value = valueMatch ? parseInt(valueMatch[1]!) : 0
5255
if (value !== lastValue) {
5356
lastValue = value
5457
// wait long enough for the server to be back online
@@ -57,6 +60,9 @@ const DevReloadActive = () => {
5760
fireLoadEvent({ reloadValue: value })
5861
setError(null)
5962
}
63+
} else if (response.ok) {
64+
console.log("dev reload endpoint didn't return magic value, disabling dev reload")
65+
return count
6066
} else {
6167
failCount++
6268
await sleep(2000)
@@ -72,6 +78,6 @@ const DevReloadActive = () => {
7278
devConnected = false
7379
}
7480
}
75-
}, [setError])
81+
}, [setError, rootUrl])
7682
return <></>
7783
}

src/python-fastui/fastui/dev.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@ async def lifespan(self, app: FastAPI):
3535
yield
3636

3737
async def dev_reload_endpoints(self):
38-
return StreamingResponse(self.ping())
38+
return StreamingResponse(self.ping(), media_type='text/plain')
3939

4040
def _on_signal(self, *_args: _t.Any):
4141
# print('setting stop', _args)
4242
self.stop.set()
4343

4444
async def ping(self):
4545
# print('connected', os.getpid())
46+
yield b'fastui-dev-reload\n'
4647
yield b'.'
4748
while True:
4849
try:

src/python-fastui/tests/test_dev.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from unittest.mock import patch
2+
3+
from fastui.dev import dev_fastapi_app
4+
from httpx import AsyncClient
5+
6+
7+
def mock_signal(_sig, on_signal):
8+
on_signal()
9+
10+
11+
async def test_dev_connect():
12+
with patch('fastui.dev.signal.signal', new=mock_signal):
13+
app = dev_fastapi_app()
14+
async with app.router.lifespan_context(app):
15+
async with AsyncClient(app=app, base_url='http://test') as client:
16+
r = await client.get('/api/__dev__/reload')
17+
assert r.status_code == 200
18+
assert r.headers['content-type'] == 'text/plain; charset=utf-8'
19+
assert r.text.startswith('fastui-dev-reload\n')

0 commit comments

Comments
 (0)