diff --git a/main.py b/main.py index 6cdbd2f..5c89bae 100644 --- a/main.py +++ b/main.py @@ -121,6 +121,7 @@ def main() -> int: # # watcher.start() # watcher.wait() # blocks until stop() is called via a signal + web_app.wait() # keep process alive while the watcher is disabled return 0 diff --git a/src/MatchesWebApp.py b/src/MatchesWebApp.py index 4c9dcef..23b525b 100644 --- a/src/MatchesWebApp.py +++ b/src/MatchesWebApp.py @@ -265,6 +265,7 @@ class MatchesWebApp: self._port = port self._build_lock = threading.Lock() self._app = Flask(__name__) + self._thread: "threading.Thread | None" = None self._register_routes() @property @@ -272,19 +273,32 @@ class MatchesWebApp: return self._app def start(self) -> threading.Thread: - """Starts the Flask server on a daemon thread and returns it.""" - thread = threading.Thread( + """ + Starts the Flask server on a background thread and returns it. + + The thread is non-daemon so the process stays alive even when the + caller does not explicitly join() — important when this is the + only foreground task (e.g. watcher disabled for testing). + """ + if self._thread is not None and self._thread.is_alive(): + return self._thread + self._thread = threading.Thread( target=self._app.run, kwargs={"host": self._host, "port": self._port, "debug": False, "use_reloader": False, "threaded": True}, name="MatchesWebApp", - daemon=True, + daemon=False, ) - thread.start() + self._thread.start() print(f"[MatchesWebApp] listening on {self._host}:{self._port}", flush=True) - return thread + return self._thread + + def wait(self) -> None: + """Blocks until the Flask thread exits (or returns immediately if not started).""" + if self._thread is not None: + self._thread.join() # ------------------------------------------------------------------ # Routes