run server in separate process

This commit is contained in:
Nick Merrill
2025-01-14 11:44:03 -05:00
parent 8d90a3afa3
commit 7b35ab844a

View File

@@ -64,8 +64,7 @@ class TestServer(Server):
# Test fixtures # Test fixtures
@pytest.fixture def make_server_app()-> Starlette:
async def server_app()-> Starlette:
"""Create test Starlette app with SSE transport""" """Create test Starlette app with SSE transport"""
sse = SseServerTransport("/messages/") sse = SseServerTransport("/messages/")
server = TestServer() server = TestServer()
@@ -93,43 +92,46 @@ def space_around_test():
yield yield
time.sleep(0.1) time.sleep(0.1)
@pytest.fixture() def run_server(server_port: int):
def server(server_app: Starlette, server_port: int): app = make_server_app()
proc = multiprocessing.Process(target=uvicorn.run, daemon=True, kwargs={ server = uvicorn.Server(config=uvicorn.Config(app=app, host="127.0.0.1", port=server_port, log_level="error"))
"app": server_app,
"host": "127.0.0.1",
"port": server_port,
"log_level": "error"
})
print(f'starting server on {server_port}') print(f'starting server on {server_port}')
proc.start() server.run()
# Give server time to start # Give server time to start
while not server.started: while not server.started:
print('waiting for server to start') print('waiting for server to start')
time.sleep(0.5) time.sleep(0.5)
try: @pytest.fixture()
yield def server(server_port: int):
finally: proc = multiprocessing.Process(target=run_server, kwargs={"server_port": server_port}, daemon=True)
print('killing server') print('starting process')
# Signal the server to stop proc.start()
server.should_exit = True
# Force close the server's main socket # Wait for server to be running
if hasattr(server.servers, "servers"): max_attempts = 20
for s in server.servers: attempt = 0
print(f'closing {s}') print('waiting for server to start')
s.close() while attempt < max_attempts:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('127.0.0.1', server_port))
break
except ConnectionRefusedError:
time.sleep(0.1)
attempt += 1
else:
raise RuntimeError("Server failed to start after {} attempts".format(max_attempts))
# Wait for thread to finish yield
proc.terminate()
proc.join(timeout=2) print('killing server')
if proc.is_alive(): # Signal the server to stop
print("Warning: Server thread did not exit cleanly") proc.kill()
# Optionally, you could add more aggressive cleanup here proc.join(timeout=2)
import _thread if proc.is_alive():
_thread.interrupt_main() print("server process failed to terminate")
@pytest.fixture() @pytest.fixture()
async def http_client(server, server_url) -> AsyncGenerator[httpx.AsyncClient, None]: async def http_client(server, server_url) -> AsyncGenerator[httpx.AsyncClient, None]: