| 76 | |
| 77 | |
| 78 | def send_rpc( |
| 79 | s: Any, |
| 80 | method: str, |
| 81 | args: dict[str, Any] | None = None, |
| 82 | request_id: int = 1, |
| 83 | timeout: float = 15.0, |
| 84 | ) -> dict[str, Any] | None: |
| 85 | params = [args] if args is not None else [{}] |
| 86 | req = {"method": method, "params": params, "id": request_id} |
| 87 | s.write((json.dumps(req, separators=(",", ":")) + "\n").encode("ascii")) |
| 88 | s.flush() |
| 89 | deadline = time.time() + timeout |
| 90 | buf = b"" |
| 91 | while time.time() < deadline: |
| 92 | chunk = s.read(s.in_waiting or 1) |
| 93 | if not chunk: |
| 94 | continue |
| 95 | buf += chunk |
| 96 | while b"\n" in buf: |
| 97 | line, _, buf = buf.partition(b"\n") |
| 98 | text = line.decode("ascii", errors="replace").strip() |
| 99 | for prefix in ("REMOTE: ", "RESULT: "): |
| 100 | if text.startswith(prefix): |
| 101 | text = text[len(prefix) :] |
| 102 | break |
| 103 | if not (text.startswith("{") and text.endswith("}")): |
| 104 | continue |
| 105 | try: |
| 106 | msg = json.loads(text) |
| 107 | except json.JSONDecodeError: |
| 108 | continue |
| 109 | if isinstance(msg, dict) and msg.get("id") == request_id: |
| 110 | return msg |
| 111 | return None |
| 112 | |
| 113 | |
| 114 | @typechecked |