Skip to content

Commit

Permalink
fix: do not reload modules that are not under the app directory
Browse files Browse the repository at this point in the history
Fixes #177
Fixes #148
  • Loading branch information
maartenbreddels committed Jun 28, 2023
1 parent 200ddc4 commit 9547fdd
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 7 deletions.
1 change: 1 addition & 0 deletions solara/server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def __init__(self, name, default_app_name="Page"):
app = self._execute()

self._first_execute_app = app
reload.reloader.root_path = self.directory
app_context.close()

def _execute(self):
Expand Down
44 changes: 37 additions & 7 deletions solara/server/reload.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
import sys
import threading
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Set, Type

NO_WATCHDOG = False
Expand Down Expand Up @@ -129,6 +130,14 @@ def __init__(self, on_change: Optional[Callable[[str], None]] = None) -> None:
self.requires_reload = False
self.ignore_modules: Set[str] = set()
self.reload_event_next = threading.Event()
# should be set at app.directory
self.root_path: Optional[Path] = None
# maybe we want this mode enabled in the future via configuration
# this is useful if you have some packages installed in editable mode
# and you want to quick reload. However this does not always work:
# * https://github.com/widgetti/solara/issues/177
# * https://github.com/widgetti/solara/issues/148
self.aggresive_reload = False

def start(self):
if self._first:
Expand All @@ -152,25 +161,46 @@ def _on_change(self, name):
def close(self):
self.watcher.close()

def get_reload_module_names(self):
if self.aggresive_reload:
# not sure why, but if we reload pandas, the integration/reload_test.py fails
return {
k for k in set(sys.modules) - set(self.ignore_modules) if not (k.startswith("solara.server") or k.startswith("anyio") or k.startswith("pandas"))
}
else:
reload = []
for name in sorted(sys.modules):
mod = sys.modules[name]
if name.startswith("solara.server"):
continue # this will break everything
if name in self.ignore_modules:
continue # nothing we imported from solara itself
if hasattr(mod, "__file__") and mod.__file__:
if not mod.__file__.startswith(str(self.root_path)):
logger.debug("Ignoring module %s", mod)
continue
else:
logger.debug("Ignoring module %s because we do not know the path", mod)
continue
reload.append(name)
return reload

def reload(self):
# before we did this:
# # don't reload modules like solara.server and react
# # that may cause issues (like 2 Element classes existing)
# not sure why, but if we reload pandas, the integration/reload_test.py fails
reload_modules = {
k for k in set(sys.modules) - set(self.ignore_modules) if not (k.startswith("solara.server") or k.startswith("anyio") or k.startswith("pandas"))
}
reload_modules = self.get_reload_module_names()
# which picks up import that are done in threads etc, but it will also reload starlette, httptools etc
# which causes issues with exceptions and isinstance checks.
# reload_modules = self.watched_modules
logger.info("Reloading modules... %s", reload_modules)
# not sure if this is needed
importlib.invalidate_caches()
for mod in sorted(reload_modules):
for mod_name in sorted(reload_modules):
# don't reload modules like solara.server and react
# that may cause issues (like 2 Element classes existing)
logger.debug("Reloading module %s", mod)
sys.modules.pop(mod, None)
logger.debug("Reloading module %s", mod_name)
sys.modules.pop(mod_name, None)
# if all successful...
self.requires_reload = False

Expand Down

0 comments on commit 9547fdd

Please sign in to comment.