pytest's default capture and Jupyter's IPython display replace
sys.stdout/sys.stderr with stream objects that don't expose a real
fileno. Server passed those to subprocess.Popen, which raised
'io.UnsupportedOperation: fileno' on the first network test (the
only one that took capsys, which forces Python-level capture).
New _resolve_subprocess_stream helper detects streams without a
working fileno() and falls back to subprocess.DEVNULL, so Server()
just works inside any pytest fixture or notebook cell. Users who
want to see the server's banner output can still pass an explicit
open(...)-backed file, or run pytest with -s.
Drive-by: drop the unused capsys param from
test_real_server_starts_and_accepts_connection — it was never
referenced and was the trigger for the original failure.
All 16 tests now pass locally with verbose output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>