Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fasthtml/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@
'fasthtml.jupyter.JupyUvi._live_sse': ('api/jupyter.html#jupyuvi._live_sse', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.JupyUvi._setup_live': ('api/jupyter.html#jupyuvi._setup_live', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.JupyUvi.start': ('api/jupyter.html#jupyuvi.start', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.JupyUvi.start_async': ('api/jupyter.html#jupyuvi.start_async', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.JupyUvi.stop': ('api/jupyter.html#jupyuvi.stop', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.JupyUviAsync': ('api/jupyter.html#jupyuviasync', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.JupyUviAsync.__init__': ( 'api/jupyter.html#jupyuviasync.__init__',
Expand All @@ -190,6 +189,7 @@
'fasthtml.jupyter.render_ft': ('api/jupyter.html#render_ft', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.show': ('api/jupyter.html#show', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.wait_port_free': ('api/jupyter.html#wait_port_free', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.wait_port_free_async': ('api/jupyter.html#wait_port_free_async', 'fasthtml/jupyter.py'),
'fasthtml.jupyter.ws_client': ('api/jupyter.html#ws_client', 'fasthtml/jupyter.py')},
'fasthtml.live_reload': {},
'fasthtml.oauth': { 'fasthtml.oauth.AppleAppClient': ('api/oauth.html#appleappclient', 'fasthtml/oauth.py'),
Expand Down
34 changes: 20 additions & 14 deletions fasthtml/jupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/06_jupyter.ipynb.

# %% auto #0
__all__ = ['nb_serve', 'nb_serve_async', 'is_port_free', 'wait_port_free', 'show', 'render_ft', 'htmx_config_port', 'JupyUvi',
'JupyUviAsync', 'HTMX', 'ws_client']
__all__ = ['nb_serve', 'nb_serve_async', 'is_port_free', 'wait_port_free', 'wait_port_free_async', 'show', 'render_ft',
'htmx_config_port', 'JupyUvi', 'JupyUviAsync', 'HTMX', 'ws_client']

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

# %% ../nbs/api/06_jupyter.ipynb #508917bc
def is_port_free(port, host='localhost'):
"Check if `port` is free on `host`"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(1)
return True
except OSError: return False
finally: sock.close()

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

async def wait_port_free_async(port, host='localhost', max_wait=20):
"Async wait for `port` to be free on `host`"
start_time = time.time()
while not is_port_free(port, host):
if time.time() - start_time > max_wait: raise TimeoutError(f"Port {host}:{port} not free after {max_wait}s")
await asyncio.sleep(0.1)


# %% ../nbs/api/06_jupyter.ipynb #654b36bb
@delegates(_show)
def show(*s, **kwargs):
Expand Down Expand Up @@ -94,12 +102,9 @@ def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, start=True
def start(self):
self.server = nb_serve(self.app, log_level=self.log_level, host=self.host, port=self.port,daemon=self.daemon, **self.kwargs)

async def start_async(self):
self.server = await nb_serve_async(self.app, log_level=self.log_level, host=self.host, port=self.port, **self.kwargs)

def stop(self):
self.server.should_exit = True
wait_port_free(self.port)
wait_port_free(self.port, self.host)

def _setup_live(self, app):
rt = self.live_rt or '/_lr'
Expand All @@ -117,7 +122,7 @@ async def _live_sse(self):
ver = self._live_ver
yield 'data: reload\n\n'

# %% ../nbs/api/06_jupyter.ipynb #9134035e
# %% ../nbs/api/06_jupyter.ipynb #f6316c73
class JupyUviAsync(JupyUvi):
"Start and stop an async Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`"
def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, **kwargs):
Expand All @@ -126,9 +131,10 @@ def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, **kwargs):
async def start(self):
self.server = await nb_serve_async(self.app, log_level=self.log_level, host=self.host, port=self.port, **self.kwargs)

def stop(self):
async def stop(self):
self.server.should_exit = True
wait_port_free(self.port)
await wait_port_free_async(self.port, self.host)


# %% ../nbs/api/06_jupyter.ipynb #a448e420
from starlette.testclient import TestClient
Expand Down
Loading
Loading