From dcc8bb9636d6c041ecde419e380255e802b0adc0 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Fri, 5 Jun 2026 20:36:56 +0200 Subject: [PATCH] Fix polling slowness by removing multiprocessing --- main.py | 4 +-- pyclient/poll.py | 86 ++++++++---------------------------------------- 2 files changed, 15 insertions(+), 75 deletions(-) diff --git a/main.py b/main.py index a8f5f50..9005a6e 100644 --- a/main.py +++ b/main.py @@ -82,8 +82,8 @@ if __name__ == "__main__": raise SystemExit(main()) c = PyClient([ - "http://localhost:5001", - "http://localhost:5002" + "http://127.0.0.1:5001", + "http://127.0.0.1:5002" ], debug=True) out = c.play_game("/tic-tac-toe", TicTacToe) diff --git a/pyclient/poll.py b/pyclient/poll.py index c99e0a7..98bfef3 100644 --- a/pyclient/poll.py +++ b/pyclient/poll.py @@ -1,63 +1,9 @@ """Discovery and polling helpers for Bot-Man-Toe servers.""" -import multiprocessing -from queue import Empty from typing import Any, Dict, Optional, Tuple, Union import requests - - -def _poll_worker(url: str, payload: Dict[str, Any], timeout: Optional[Union[float, Tuple[float, float]]], queue: multiprocessing.Queue) -> None: - """Run a polling request in a separate process.""" - try: - response = requests.get(url, json=payload, timeout=timeout) - response.raise_for_status() - content = response.json() - if isinstance(content, dict): - queue.put(("ok", content)) - return - except requests.exceptions.RequestException: - queue.put(("request_error", None)) - return - except ValueError: - queue.put(("invalid", None)) - return - - queue.put(("invalid", None)) - - -def _poll_request( - url: str, - payload: Dict[str, Any], - request_timeout: Optional[Union[float, Tuple[float, float]]], - deadline: float, -) -> tuple[str, Optional[Dict[str, Any]]]: - """Execute a bounded JSON request and classify the result.""" - context = multiprocessing.get_context("spawn") - queue: multiprocessing.Queue = context.Queue(maxsize=1) - process = context.Process(target=_poll_worker, args=(url, payload, request_timeout, queue)) - process.daemon = True - process.start() - - process.join(deadline) - if process.is_alive(): - process.terminate() - process.join() - queue.close() - queue.join_thread() - return ("timeout", None) - - try: - status, content = queue.get(timeout=0.1) - except Empty: - queue.close() - queue.join_thread() - return ("invalid", None) - - queue.close() - queue.join_thread() - return status, content - +import time class ServerAgent: """Representation of a server that can host one or more games.""" @@ -104,19 +50,11 @@ class ServerAgent: :raises ValueError: If the response body is not a JSON object or if the payload contains malformed discovery fields. """ - if timeout is None: - deadline = 10.0 - elif isinstance(timeout, (int, float)): - deadline = float(timeout) - else: - deadline = sum(timeout) - status, content = _poll_request(url.rstrip("/") + "/", {}, timeout, deadline) + response = requests.get(url.rstrip("/") + "/", timeout=timeout) + response.raise_for_status() + content = response.json() - if status == "timeout": - raise requests.exceptions.RequestException("Server discovery request timed out.") - if status == "request_error": - raise requests.exceptions.RequestException("Server discovery request failed.") - if status != "ok" or content is None: + if not isinstance(content, dict): raise ValueError("Server discovery responses must be JSON objects.") raw_name = content.get("name", "") @@ -150,13 +88,15 @@ class ServerAgent: """ url = f"{self.url.rstrip('/')}/{game.lstrip('/')}" - status, content = _poll_request(url, payload, timeout, 3.0) + try: + response = requests.get(url, json=payload, timeout=timeout) + response.raise_for_status() + content = response.json() + except (requests.exceptions.RequestException, ValueError): + return None if self.debug: print(f"[DBG] Agent `{self.name}` returned:") - print(( status, content )) + print(content) - if status != "ok": - return None - - return content + return content if isinstance(content, dict) else None