diff --git a/pyproject.toml b/pyproject.toml index 17e4698..47bac87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,14 +22,14 @@ dev = [ "httpx>=0.27", "fastapi>=0.110", "uvicorn>=0.29", - "mcp>=1.2", + "mcp>=1.2,<2", ] api = [ "fastapi>=0.110", "uvicorn>=0.29", ] mcp = [ - "mcp>=1.2", + "mcp>=1.2,<2", ] render = [ "manim>=0.19", diff --git a/tests/test_backends.py b/tests/test_backends.py index 16fc0dd..0ff1079 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -35,6 +35,13 @@ class ResponseBody: def read(self): return b'{"error":"bad auth"}' + def close(self): + # HTTPError wraps fp in a finalizer that calls close() on GC. Without + # this, the AttributeError surfaces as an unraisable exception inside + # whatever unrelated test happens to be running when the collector + # gets to it. + pass + def raise_httperror(*args, **kwargs): raise HTTPError( url="https://example.test/v1/chat/completions", diff --git a/tests/test_sol_staged_pipeline.py b/tests/test_sol_staged_pipeline.py index 28e33fd..0c66111 100644 --- a/tests/test_sol_staged_pipeline.py +++ b/tests/test_sol_staged_pipeline.py @@ -3,6 +3,7 @@ import subprocess import sys import threading +import time from pathlib import Path import pytest @@ -66,9 +67,21 @@ def __init__(self): self.returncode = 0 def wait(self, timeout=None): - observed["trace_during_wait"] = ( - tmp_path / "trace.jsonl" - ).read_text(encoding="utf-8") + # CodexCli streams stdout to the trace from a daemon thread it only + # joins after wait() returns, so nothing orders that thread against + # this call. Poll instead of reading once: the point of the + # assertion is that the trace fills during the run rather than + # afterwards, and a bare read just races the reader thread. + trace = tmp_path / "trace.jsonl" + deadline = time.monotonic() + 10.0 + text = "" + while time.monotonic() < deadline: + if trace.is_file(): + text = trace.read_text(encoding="utf-8") + if "thread.started" in text: + break + time.sleep(0.01) + observed["trace_during_wait"] = text return self.returncode def kill(self):