Skip to content

Commit 25e3df6

Browse files
committed
Call module auto loader from Streamlit's path watcher
1 parent fe35d3d commit 25e3df6

File tree

2 files changed

+31
-16
lines changed

2 files changed

+31
-16
lines changed

packages/kernel/py/stlite-lib/stlite_lib/server/server.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def __init__(self, main_script_path: str, app_home_dir: str | None = None) -> No
6161

6262
self._runtime.stats_mgr.register_provider(self._media_file_storage)
6363

64-
async def start(self) -> None:
64+
async def start(self, file_change_callback: Callable[[str], None] | None = None) -> None:
6565
"""Start the server.
6666
6767
When this returns, Streamlit is ready to accept new sessions.
@@ -72,7 +72,7 @@ async def start(self) -> None:
7272
home_dir_contextvar.set(self.app_home_dir)
7373

7474
# In stlite, we deal with WebSocket separately.
75-
self._websocket_handler = WebSocketHandler(self._runtime)
75+
self._websocket_handler = WebSocketHandler(self._runtime, file_change_callback)
7676

7777
# Based on the original impl at https://github.com/streamlit/streamlit/blob/1.18.1/lib/streamlit/web/server/server.py#L221 # noqa: E501
7878
base = "" # The original impl reads the `server.baseUrlPath` config, but we use a fixed empty string. # noqa: E501
@@ -258,10 +258,15 @@ class WebSocketHandler(SessionClient):
258258

259259
_callback: Callable[[bytes | str, bool], None] | None
260260

261-
def __init__(self, runtime: Runtime) -> None:
261+
def __init__(self, runtime: Runtime, file_change_callback: Callable[[str], None] | None = None) -> None:
262262
self._runtime = runtime
263263
self._session_id = None
264264

265+
# File change callback needs to be registered in this handler's `open()`
266+
# because the callback is registered via the `AppSession` instance
267+
# which is created after the connection is established in `open()`.
268+
self._file_change_callback = file_change_callback
269+
265270
def write_forward_msg(self, msg: ForwardMsg) -> None:
266271
"""Send a ForwardMsg to the browser."""
267272
if self._callback is None:
@@ -282,6 +287,18 @@ def open(self, on_message: Callable[[bytes | str, bool], None]) -> None:
282287
existing_session_id=existing_session_id,
283288
)
284289

290+
if self._file_change_callback:
291+
session_info = self._runtime._session_mgr.get_session_info(self._session_id)
292+
if session_info is None:
293+
_LOGGER.warning("No session info found. Cannot register file change callback.")
294+
return
295+
session = session_info.session
296+
if session is None:
297+
_LOGGER.warning("No session found. Cannot register file change callback.")
298+
return
299+
_LOGGER.debug("Registering file change callback for session %s", session.id)
300+
session._local_sources_watcher.register_file_change_callback(self._file_change_callback)
301+
285302
def on_close(self) -> None:
286303
if not self._session_id:
287304
return

packages/kernel/src/worker-runtime.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,23 @@ __bootstrap__`); // This last line evaluates to the function so it is returned f
369369
);
370370
console.debug("Set up the Streamlit configuration");
371371

372+
const fileChangeCallback = moduleAutoLoad
373+
? (filePath: string) => {
374+
console.debug("File changed", filePath);
375+
if (filePath.endsWith(".py")) {
376+
const fileData = pyodide.FS.readFile(filePath, { encoding: "utf8" });
377+
dispatchModuleAutoLoading(pyodide, onModuleAutoLoad, [fileData]);
378+
}
379+
}
380+
: undefined;
381+
372382
console.debug("Booting up the Streamlit server");
373383
const Server = pyodide.pyimport("stlite_lib.server.Server");
374384
const httpServer = Server(
375385
canonicalEntrypoint,
376386
appId ? getAppHomeDir(appId) : null,
377387
);
378-
await httpServer.start();
388+
await httpServer.start(fileChangeCallback);
379389
console.debug("Booted up the Streamlit server");
380390

381391
return {
@@ -607,18 +617,6 @@ export function startWorkerEnv(
607617
const { path: rawPath, data: fileData, opts } = msg.data;
608618
const path = resolveAppPath(appId, rawPath);
609619

610-
if (
611-
moduleAutoLoad &&
612-
typeof fileData === "string" &&
613-
path.endsWith(".py")
614-
) {
615-
// Auto-install must be dispatched before writing the file
616-
// because its promise should be set before saving the file triggers rerunning.
617-
console.debug(`Auto install the requirements in ${path}`);
618-
619-
dispatchModuleAutoLoading(pyodide, onModuleAutoLoad, [fileData]);
620-
}
621-
622620
console.debug(`Write a file "${path}"`);
623621
writeFileWithParents(pyodide, path, fileData, opts);
624622
reply({

0 commit comments

Comments
 (0)