Reload services on debounced file edits and replacement saves
This commit is contained in:
parent
15e4ed81a2
commit
dc24c4d4fe
2 changed files with 77 additions and 9 deletions
|
|
@ -11,7 +11,6 @@ import uvicorn
|
|||
from uvicorn._subprocess import get_subprocess
|
||||
from uvicorn.config import LOGGING_CONFIG, Config
|
||||
from uvicorn.supervisors import BaseReload, Multiprocess
|
||||
from watchdog.events import FileSystemEvent, PatternMatchingEventHandler
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from great_ai.constants import SERVER_NAME
|
||||
|
|
@ -20,6 +19,7 @@ from great_ai.deploy import GreatAI
|
|||
from great_ai.errors import ArgumentValidationError, MissingArgumentError
|
||||
from great_ai.utilities import get_logger
|
||||
|
||||
from .deploy.reload_event_handler import ReloadEventHandler
|
||||
from .parse_arguments import parse_arguments
|
||||
|
||||
logger = get_logger(SERVER_NAME)
|
||||
|
|
@ -83,18 +83,12 @@ def serve() -> None:
|
|||
pass
|
||||
else:
|
||||
|
||||
class EventHandler(PatternMatchingEventHandler):
|
||||
class EventHandler(ReloadEventHandler):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(
|
||||
patterns=["*.py", "*.ipynb"], ignore_patterns=["__*.py"]
|
||||
)
|
||||
super().__init__(self.restart)
|
||||
self.server: Optional[GreatAIReload] = None
|
||||
self.restart()
|
||||
|
||||
def on_closed(self, event: FileSystemEvent) -> None:
|
||||
logger.warning(f"File {event.src_path} has triggered a restart")
|
||||
self.restart()
|
||||
|
||||
def restart(self) -> None:
|
||||
file_name = get_script_name(args.file_name)
|
||||
app = find_app(file_name)
|
||||
|
|
@ -124,6 +118,7 @@ def serve() -> None:
|
|||
Event().wait()
|
||||
finally:
|
||||
observer.stop()
|
||||
restart_handler.close()
|
||||
restart_handler.stop_server()
|
||||
if args.file_name.endswith(".ipynb"):
|
||||
try:
|
||||
|
|
|
|||
46
great_ai/deploy/reload_event_handler.py
Normal file
46
great_ai/deploy/reload_event_handler.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from threading import RLock, Timer
|
||||
from typing import Callable, Optional
|
||||
|
||||
from watchdog.events import FileSystemEvent, PatternMatchingEventHandler
|
||||
|
||||
|
||||
class ReloadEventHandler(PatternMatchingEventHandler):
|
||||
"""Debounce edit and replacement events emitted by supported OS observers."""
|
||||
|
||||
def __init__(
|
||||
self, restart: Callable[[], None], debounce_seconds: float = 0.3
|
||||
) -> None:
|
||||
super().__init__(patterns=["*.py", "*.ipynb"], ignore_patterns=["__*.py"])
|
||||
self._restart = restart
|
||||
self._debounce_seconds = debounce_seconds
|
||||
self._lock = RLock()
|
||||
self._timer: Optional[Timer] = None
|
||||
self._closed = False
|
||||
|
||||
def on_any_event(self, event: FileSystemEvent) -> None:
|
||||
if event.is_directory or event.event_type not in {
|
||||
"created",
|
||||
"modified",
|
||||
"moved",
|
||||
"closed",
|
||||
}:
|
||||
return
|
||||
with self._lock:
|
||||
if self._closed:
|
||||
return
|
||||
if self._timer is not None:
|
||||
self._timer.cancel()
|
||||
self._timer = Timer(self._debounce_seconds, self._run_restart)
|
||||
self._timer.daemon = True
|
||||
self._timer.start()
|
||||
|
||||
def _run_restart(self) -> None:
|
||||
with self._lock:
|
||||
if not self._closed:
|
||||
self._restart()
|
||||
|
||||
def close(self) -> None:
|
||||
with self._lock:
|
||||
self._closed = True
|
||||
if self._timer is not None:
|
||||
self._timer.cancel()
|
||||
Loading…
Reference in a new issue