Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions pathview/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,22 @@ def send_message(self, msg: dict) -> None:
self.process.stdin.flush()

def read_line(self) -> dict | None:
"""Read one JSON line from the subprocess stdout."""
line = self.process.stdout.readline()
if not line:
return None
return json.loads(line.strip())
"""Read one JSON line from the subprocess stdout.

Skips blank or non-JSON lines that may leak from native C/C++
libraries writing directly to the OS-level stdout fd.
"""
while True:
line = self.process.stdout.readline()
if not line:
return None
stripped = line.strip()
if not stripped:
continue
try:
return json.loads(stripped)
except json.JSONDecodeError:
continue

def read_line_timeout(self, timeout: float = EXEC_TIMEOUT) -> dict | None:
"""Read one JSON line with a timeout. Returns None on EOF or timeout.
Expand Down
2 changes: 2 additions & 0 deletions pathview/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def open_browser_when_ready():
if args.debug:
app.run(host=args.host, port=args.port, debug=True, threaded=True)
else:
import logging
logging.getLogger("waitress.queue").setLevel(logging.ERROR)
from waitress import serve
serve(app, host=args.host, port=args.port, threads=4)
except KeyboardInterrupt:
Expand Down
11 changes: 10 additions & 1 deletion pathview/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""

import sys
import os
import json
import subprocess
import threading
Expand All @@ -26,7 +27,15 @@
_stdout_lock = threading.Lock()

# Keep a reference to the real stdout pipe — protocol messages go here.
_real_stdout = sys.stdout
# We dup() fd 1 so that _real_stdout survives the fd-level redirect below.
_real_stdout_fd = os.dup(1)
_real_stdout = os.fdopen(_real_stdout_fd, "w")

# Redirect OS-level fd 1 to devnull so that C/C++ libraries (e.g. jsbsim)
# writing directly to stdout via printf/cout cannot corrupt the JSON protocol.
_devnull_fd = os.open(os.devnull, os.O_WRONLY)
os.dup2(_devnull_fd, 1)
os.close(_devnull_fd)

# Worker state
_namespace = {}
Expand Down