Initial upload: mockapi - OpenAPI Mock Server Generator
This commit is contained in:
137
src/mockapi/core/hot_reload.py
Normal file
137
src/mockapi/core/hot_reload.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""Hot-Reload functionality for spec file changes."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
|
||||
class HotReloadHandler(FileSystemEventHandler):
|
||||
"""Handler for file system events."""
|
||||
|
||||
def __init__(self, spec_file: str, debounce_ms: int = 500):
|
||||
"""Initialize the handler.
|
||||
|
||||
Args:
|
||||
spec_file: Path to the spec file to watch
|
||||
debounce_ms: Debounce delay in milliseconds
|
||||
"""
|
||||
super().__init__()
|
||||
self.spec_file = Path(spec_file).resolve()
|
||||
self.debounce_ms = debounce_ms
|
||||
self.last_reload_time = 0
|
||||
self.reload_callback: Optional[callable] = None
|
||||
|
||||
def on_modified(self, event):
|
||||
"""Handle file modification events."""
|
||||
if event.is_directory:
|
||||
return
|
||||
|
||||
event_path = Path(event.src_path).resolve()
|
||||
|
||||
if event_path == self.spec_file:
|
||||
current_time = time.time() * 1000
|
||||
if current_time - self.last_reload_time > self.debounce_ms:
|
||||
self.last_reload_time = current_time
|
||||
self._trigger_reload()
|
||||
|
||||
def _trigger_reload(self):
|
||||
"""Trigger a reload."""
|
||||
if self.reload_callback:
|
||||
self.reload_callback()
|
||||
|
||||
|
||||
class HotReloader:
|
||||
"""Watches spec file and restarts server on changes."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
spec_file: str,
|
||||
port: int = 8080,
|
||||
host: str = "0.0.0.0",
|
||||
debounce_ms: int = 500,
|
||||
):
|
||||
"""Initialize the hot reloader.
|
||||
|
||||
Args:
|
||||
spec_file: Path to the OpenAPI spec file
|
||||
port: Server port
|
||||
host: Server host
|
||||
debounce_ms: Debounce delay for rapid changes
|
||||
"""
|
||||
self.spec_file = Path(spec_file).resolve()
|
||||
self.port = port
|
||||
self.host = host
|
||||
self.debounce_ms = debounce_ms
|
||||
|
||||
self.observer: Optional[Observer] = None
|
||||
self.server_process: Optional[subprocess.Popen] = None
|
||||
self._stop_event = threading.Event()
|
||||
|
||||
def start_watching(self):
|
||||
"""Start watching for file changes and run server."""
|
||||
handler = HotReloadHandler(str(self.spec_file), self.debounce_ms)
|
||||
handler.reload_callback = self._on_spec_changed
|
||||
|
||||
self.observer = Observer()
|
||||
self.observer.schedule(
|
||||
handler,
|
||||
str(self.spec_file.parent),
|
||||
recursive=False,
|
||||
)
|
||||
self.observer.start()
|
||||
|
||||
self._start_server()
|
||||
|
||||
try:
|
||||
while not self._stop_event.is_set():
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
"""Stop watching and terminate server."""
|
||||
self._stop_event.set()
|
||||
|
||||
if self.observer:
|
||||
self.observer.stop()
|
||||
self.observer.join()
|
||||
|
||||
if self.server_process:
|
||||
self.server_process.terminate()
|
||||
self.server_process.wait()
|
||||
|
||||
def _start_server(self):
|
||||
"""Start the mock server process."""
|
||||
print(f"Starting mock server on {self.host}:{self.port}")
|
||||
|
||||
self.server_process = subprocess.Popen(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"uvicorn",
|
||||
"mockapi.core.server_generator:create_mock_server",
|
||||
"--host",
|
||||
self.host,
|
||||
"--port",
|
||||
str(self.port),
|
||||
],
|
||||
env={**os.environ, "MOCKAPI_SPEC": str(self.spec_file)},
|
||||
)
|
||||
|
||||
def _on_spec_changed(self):
|
||||
"""Handle spec file changes."""
|
||||
print(f"\nSpec file changed: {self.spec_file}")
|
||||
print("Reloading server...")
|
||||
|
||||
if self.server_process:
|
||||
self.server_process.terminate()
|
||||
self.server_process.wait()
|
||||
|
||||
self._start_server()
|
||||
Reference in New Issue
Block a user