From cc619111d08e7272feb125e4160907a870d3d450 Mon Sep 17 00:00:00 2001 From: DIEHARDERS Date: Fri, 1 Aug 2025 18:01:33 -0700 Subject: [PATCH 1/3] fix python error when stopping sidecar --- src/backends/main.py | 74 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/src/backends/main.py b/src/backends/main.py index 1ccfe1e..1a7dd93 100644 --- a/src/backends/main.py +++ b/src/backends/main.py @@ -64,8 +64,16 @@ def kill_process(): os.kill(os.getpid(), signal.SIGINT) # This force closes this script. +# Graceful shutdown handler +async def shutdown_server(): + global server_instance + if server_instance: + print("[sidecar] Shutting down server gracefully...", flush=True) + await server_instance.shutdown() + + # Programmatically startup the api server -def start_api_server(**kwargs): +async def start_api_server_async(**kwargs): global server_instance port = kwargs.get("port", PORT_API) try: @@ -74,32 +82,56 @@ def start_api_server(**kwargs): config = Config(app, host="0.0.0.0", port=port, log_level="info") server_instance = Server(config) # Start the ASGI server - asyncio.run(server_instance.serve()) + await server_instance.serve() else: print( "[sidecar] Failed to start new server. Server instance already running.", flush=True, ) + except KeyboardInterrupt: + print("[sidecar] Received shutdown signal, stopping server...", flush=True) + await shutdown_server() except Exception as e: print(f"[sidecar] Error, failed to start API server {e}", flush=True) +def start_api_server(**kwargs): + try: + asyncio.run(start_api_server_async(**kwargs)) + except KeyboardInterrupt: + print("[sidecar] Server shutdown complete.", flush=True) + except Exception as e: + print(f"[sidecar] Unexpected error during server operation: {e}", flush=True) + + # Handle the stdin event loop. This can be used like a CLI. def stdin_loop(): print("[sidecar] Waiting for commands...", flush=True) while True: - # Read input from stdin. - user_input = sys.stdin.readline().strip() - - # Check if the input matches one of the available functions - match user_input: - case "sidecar shutdown": - print("[sidecar] Received 'sidecar shutdown' command.", flush=True) - kill_process() - case _: - print( - f"[sidecar] Invalid command [{user_input}]. Try again.", flush=True - ) + try: + # Read input from stdin. + user_input = sys.stdin.readline().strip() + + # If stdin is closed (EOF), break the loop + if not user_input and sys.stdin.closed: + break + + # Check if the input matches one of the available functions + match user_input: + case "sidecar shutdown": + print("[sidecar] Received 'sidecar shutdown' command.", flush=True) + kill_process() + case _: + if user_input: # Only print if there's actual input + print( + f"[sidecar] Invalid command [{user_input}]. Try again.", + flush=True, + ) + except (EOFError, KeyboardInterrupt): + print("[sidecar] Input handler shutting down...", flush=True) + break + except Exception as e: + print(f"[sidecar] Error in input handler: {e}", flush=True) # Start the input loop in a separate thread @@ -108,11 +140,21 @@ def start_input_thread(): input_thread = threading.Thread(target=stdin_loop) input_thread.daemon = True # so it exits when the main program exits input_thread.start() - except: - print("[sidecar] Failed to start input handler.", flush=True) + except Exception as e: + print(f"[sidecar] Failed to start input handler: {e}", flush=True) + + +# Signal handler for graceful shutdown +def signal_handler(signum, frame): + print(f"[sidecar] Received signal {signum}, initiating shutdown...", flush=True) + sys.exit(0) if __name__ == "__main__": + # Set up signal handlers for graceful shutdown + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + # You can spawn sub-processes here before the main process. # new_command = ["python", "-m", "some_script", "--arg", "argValue"] # subprocess.Popen(new_command) From 26660a0d6f47d48c5e623087fa55f1eb24faf81f Mon Sep 17 00:00:00 2001 From: DIEHARDERS Date: Fri, 1 Aug 2025 18:40:42 -0700 Subject: [PATCH 2/3] add pydantic to requirements. Pin the uvicorn ver. --- requirements.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 78569e0..0c24414 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ fastapi==0.95.2 -uvicorn[standard] +uvicorn[standard]==0.32.1 sse-starlette==1.6.5 -pyinstaller==5.13.0 \ No newline at end of file +pyinstaller==5.13.0 +pydantic==1.10.19 \ No newline at end of file From 2d88756a7046e5d5562476ee2972133a40042d39 Mon Sep 17 00:00:00 2001 From: DIEHARDERS Date: Fri, 1 Aug 2025 18:43:59 -0700 Subject: [PATCH 3/3] add space --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0c24414..c2ebefa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ fastapi==0.95.2 uvicorn[standard]==0.32.1 sse-starlette==1.6.5 pyinstaller==5.13.0 -pydantic==1.10.19 \ No newline at end of file +pydantic==1.10.19