Wait for and parse JSON-RPC response with mandatory ID matching (async). Args: timeout: Maximum time to wait in seconds expected_id: Expected request ID for JSON-RPC 2.0 correlation (mandatory) Returns: RpcResponse with parsed data and matching r
(self, timeout: float, expected_id: int)
| 556 | ) |
| 557 | |
| 558 | async def _wait_for_response(self, timeout: float, expected_id: int) -> RpcResponse: |
| 559 | """Wait for and parse JSON-RPC response with mandatory ID matching (async). |
| 560 | |
| 561 | Args: |
| 562 | timeout: Maximum time to wait in seconds |
| 563 | expected_id: Expected request ID for JSON-RPC 2.0 correlation (mandatory) |
| 564 | |
| 565 | Returns: |
| 566 | RpcResponse with parsed data and matching request ID (ID always present) |
| 567 | |
| 568 | Raises: |
| 569 | RpcTimeoutError: If no matching response within timeout |
| 570 | """ |
| 571 | assert self._serial is not None |
| 572 | |
| 573 | start = time.time() |
| 574 | |
| 575 | try: |
| 576 | async for line in self._serial.read_lines(timeout=timeout): |
| 577 | self._check_interrupt() |
| 578 | |
| 579 | if not line.startswith(self.RESPONSE_PREFIX): |
| 580 | if self.verbose: |
| 581 | print(f" [async-serial] {line[:200]}") |
| 582 | # Feed line to crash decoder if available. |
| 583 | if self._crash_decoder is not None: |
| 584 | decoded = self._crash_decoder.process_line(line) |
| 585 | if decoded: |
| 586 | for dl in decoded: |
| 587 | print(dl) |
| 588 | raise RpcCrashError( |
| 589 | "Device crashed during RPC operation", |
| 590 | decoded_lines=decoded, |
| 591 | ) |
| 592 | # Check timeout for non-REMOTE lines |
| 593 | if time.time() - start >= timeout: |
| 594 | break |
| 595 | continue |
| 596 | |
| 597 | # Line starts with RESPONSE_PREFIX - parse as JSON-RPC response |
| 598 | json_str = line[len(self.RESPONSE_PREFIX) :] |
| 599 | try: |
| 600 | data = json.loads(json_str) |
| 601 | |
| 602 | # Check if response ID matches expected ID (JSON-RPC 2.0 correlation) |
| 603 | # ID is mandatory - must match exactly |
| 604 | response_id = data.get("id") |
| 605 | if response_id is None: |
| 606 | # Responses without ID are invalid - skip them |
| 607 | continue |
| 608 | if response_id != expected_id: |
| 609 | # Skip responses that don't match our expected request ID |
| 610 | continue |
| 611 | |
| 612 | # JSON-RPC 2.0: "error" and "result" are mutually exclusive |
| 613 | # If "error" is present, raise RpcError immediately |
| 614 | if "error" in data: |
| 615 | error_obj = data["error"] |
no test coverage detected