Skip to content

Commit 8d66f21

Browse files
authored
Merge pull request #879 from AnswerDotAI/jupyuvi-wait
fixed await issues
2 parents 516b4bd + 6c01435 commit 8d66f21

3 files changed

Lines changed: 118 additions & 198 deletions

File tree

fasthtml/_modidx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@
176176
'fasthtml.jupyter.JupyUvi._live_sse': ('api/jupyter.html#jupyuvi._live_sse', 'fasthtml/jupyter.py'),
177177
'fasthtml.jupyter.JupyUvi._setup_live': ('api/jupyter.html#jupyuvi._setup_live', 'fasthtml/jupyter.py'),
178178
'fasthtml.jupyter.JupyUvi.start': ('api/jupyter.html#jupyuvi.start', 'fasthtml/jupyter.py'),
179-
'fasthtml.jupyter.JupyUvi.start_async': ('api/jupyter.html#jupyuvi.start_async', 'fasthtml/jupyter.py'),
180179
'fasthtml.jupyter.JupyUvi.stop': ('api/jupyter.html#jupyuvi.stop', 'fasthtml/jupyter.py'),
181180
'fasthtml.jupyter.JupyUviAsync': ('api/jupyter.html#jupyuviasync', 'fasthtml/jupyter.py'),
182181
'fasthtml.jupyter.JupyUviAsync.__init__': ( 'api/jupyter.html#jupyuviasync.__init__',
@@ -190,6 +189,7 @@
190189
'fasthtml.jupyter.render_ft': ('api/jupyter.html#render_ft', 'fasthtml/jupyter.py'),
191190
'fasthtml.jupyter.show': ('api/jupyter.html#show', 'fasthtml/jupyter.py'),
192191
'fasthtml.jupyter.wait_port_free': ('api/jupyter.html#wait_port_free', 'fasthtml/jupyter.py'),
192+
'fasthtml.jupyter.wait_port_free_async': ('api/jupyter.html#wait_port_free_async', 'fasthtml/jupyter.py'),
193193
'fasthtml.jupyter.ws_client': ('api/jupyter.html#ws_client', 'fasthtml/jupyter.py')},
194194
'fasthtml.live_reload': {},
195195
'fasthtml.oauth': { 'fasthtml.oauth.AppleAppClient': ('api/oauth.html#appleappclient', 'fasthtml/oauth.py'),

fasthtml/jupyter.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/06_jupyter.ipynb.
66

77
# %% auto #0
8-
__all__ = ['nb_serve', 'nb_serve_async', 'is_port_free', 'wait_port_free', 'show', 'render_ft', 'htmx_config_port', 'JupyUvi',
9-
'JupyUviAsync', 'HTMX', 'ws_client']
8+
__all__ = ['nb_serve', 'nb_serve_async', 'is_port_free', 'wait_port_free', 'wait_port_free_async', 'show', 'render_ft',
9+
'htmx_config_port', 'JupyUvi', 'JupyUviAsync', 'HTMX', 'ws_client']
1010

1111
# %% ../nbs/api/06_jupyter.ipynb #2c69d9d0
1212
import asyncio, socket, time, uvicorn
@@ -38,23 +38,31 @@ async def nb_serve_async(app, log_level="error", port=8000, host='0.0.0.0', **kw
3838

3939
# %% ../nbs/api/06_jupyter.ipynb #508917bc
4040
def is_port_free(port, host='localhost'):
41-
"Check if `port` is free on `host`"
4241
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
42+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
4343
try:
44-
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
4544
sock.bind((host, port))
45+
sock.listen(1)
4646
return True
4747
except OSError: return False
4848
finally: sock.close()
4949

5050
# %% ../nbs/api/06_jupyter.ipynb #1779cb76
51-
def wait_port_free(port, host='localhost', max_wait=3):
51+
def wait_port_free(port, host='localhost', max_wait=20):
5252
"Wait for `port` to be free on `host`"
5353
start_time = time.time()
54-
while not is_port_free(port):
55-
if time.time() - start_time>max_wait: return print(f"Timeout")
54+
while not is_port_free(port, host):
55+
if time.time() - start_time > max_wait: raise TimeoutError(f"Port {host}:{port} not free after {max_wait}s")
5656
time.sleep(0.1)
5757

58+
async def wait_port_free_async(port, host='localhost', max_wait=20):
59+
"Async wait for `port` to be free on `host`"
60+
start_time = time.time()
61+
while not is_port_free(port, host):
62+
if time.time() - start_time > max_wait: raise TimeoutError(f"Port {host}:{port} not free after {max_wait}s")
63+
await asyncio.sleep(0.1)
64+
65+
5866
# %% ../nbs/api/06_jupyter.ipynb #654b36bb
5967
@delegates(_show)
6068
def show(*s, **kwargs):
@@ -96,12 +104,9 @@ def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, start=True
96104
def start(self):
97105
self.server = nb_serve(self.app, log_level=self.log_level, host=self.host, port=self.port,daemon=self.daemon, **self.kwargs)
98106

99-
async def start_async(self):
100-
self.server = await nb_serve_async(self.app, log_level=self.log_level, host=self.host, port=self.port, **self.kwargs)
101-
102107
def stop(self):
103108
self.server.should_exit = True
104-
wait_port_free(self.port)
109+
wait_port_free(self.port, self.host)
105110

106111
def _setup_live(self, app):
107112
rt = self.live_rt or '/_lr'
@@ -119,7 +124,7 @@ async def _live_sse(self):
119124
ver = self._live_ver
120125
yield 'data: reload\n\n'
121126

122-
# %% ../nbs/api/06_jupyter.ipynb #9134035e
127+
# %% ../nbs/api/06_jupyter.ipynb #f6316c73
123128
class JupyUviAsync(JupyUvi):
124129
"Start and stop an async Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`"
125130
def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, **kwargs):
@@ -128,9 +133,10 @@ def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, **kwargs):
128133
async def start(self):
129134
self.server = await nb_serve_async(self.app, log_level=self.log_level, host=self.host, port=self.port, **self.kwargs)
130135

131-
def stop(self):
136+
async def stop(self):
132137
self.server.should_exit = True
133-
wait_port_free(self.port)
138+
await wait_port_free_async(self.port, self.host)
139+
134140

135141
# %% ../nbs/api/06_jupyter.ipynb #a448e420
136142
from starlette.testclient import TestClient

0 commit comments

Comments
 (0)