|
7 | 7 | import time |
8 | 8 | from collections import defaultdict |
9 | 9 | from threading import Lock |
| 10 | +from typing import Any |
10 | 11 |
|
11 | 12 | import pytest |
12 | 13 |
|
@@ -75,6 +76,7 @@ def pytest_runtest_call(item): |
75 | 76 | request_obj = getattr(item, "_request", None) |
76 | 77 | if request_obj is not None and "request" not in item.funcargs: |
77 | 78 | item.funcargs["request"] = request_obj |
| 79 | + if request_obj is not None and "request" not in item._fixtureinfo.argnames: |
78 | 80 | # Create a new FuncFixtureInfo instance with updated argnames |
79 | 81 | item._fixtureinfo = type(item._fixtureinfo)( |
80 | 82 | argnames=item._fixtureinfo.argnames + ("request",), |
@@ -133,27 +135,11 @@ def update_process_status(self, process_id, status): |
133 | 135 |
|
134 | 136 | with self.status_lock: |
135 | 137 | current_status = self.process_status.get(process_id, {}) |
136 | | - if status.get("feedback"): |
137 | | - current_status["feedback"] = { |
138 | | - **current_status.get("feedback", {}), |
139 | | - **status.pop("feedback"), |
140 | | - } |
141 | | - if status.get("inputs"): |
142 | | - current_status["inputs"] = { |
143 | | - **current_status.get("inputs", {}), |
144 | | - **status.pop("inputs"), |
145 | | - } |
146 | | - if status.get("reference_outputs"): |
147 | | - current_status["reference_outputs"] = { |
148 | | - **current_status.get("reference_outputs", {}), |
149 | | - **status.pop("reference_outputs"), |
150 | | - } |
151 | | - if status.get("outputs"): |
152 | | - current_status["outputs"] = { |
153 | | - **current_status.get("outputs", {}), |
154 | | - **status.pop("outputs"), |
155 | | - } |
156 | | - self.process_status[process_id] = {**current_status, **status} |
| 138 | + self.process_status[process_id] = _merge_statuses( |
| 139 | + status, |
| 140 | + current_status, |
| 141 | + unpack=["feedback", "inputs", "reference_outputs", "outputs"], |
| 142 | + ) |
157 | 143 | self.live.update(self.generate_tables()) |
158 | 144 |
|
159 | 145 | def pytest_runtest_logstart(self, nodeid): |
@@ -246,9 +232,11 @@ def _generate_table(self, suite_name: str): |
246 | 232 | f"{_abbreviate(k, max_len=max_dynamic_col_width)}: {int(v) if isinstance(v, bool) else v}" # noqa: E501 |
247 | 233 | for k, v in status.get("feedback", {}).items() |
248 | 234 | ) |
249 | | - inputs = json.dumps(status.get("inputs", {})) |
250 | | - reference_outputs = json.dumps(status.get("reference_outputs", {})) |
251 | | - outputs = json.dumps(status.get("outputs", {})) |
| 235 | + inputs = _dumps_with_fallback(status.get("inputs", {})) |
| 236 | + reference_outputs = _dumps_with_fallback( |
| 237 | + status.get("reference_outputs", {}) |
| 238 | + ) |
| 239 | + outputs = _dumps_with_fallback(status.get("outputs", {})) |
252 | 240 | table.add_row( |
253 | 241 | _abbreviate_test_name(str(pid), max_len=max_dynamic_col_width), |
254 | 242 | _abbreviate(inputs, max_len=max_dynamic_col_width), |
@@ -339,3 +327,21 @@ def _abbreviate_test_name(test_name: str, max_len: int) -> str: |
339 | 327 | return "..." + file[-file_len:] + "::" + test |
340 | 328 | else: |
341 | 329 | return test_name |
| 330 | + |
| 331 | + |
| 332 | +def _merge_statuses(update: dict, current: dict, *, unpack: list[str]) -> dict: |
| 333 | + for path in unpack: |
| 334 | + if path_update := update.pop(path, None): |
| 335 | + path_current = current.get(path, {}) |
| 336 | + if isinstance(path_update, dict) and isinstance(path_current, dict): |
| 337 | + current[path] = {**path_current, **path_update} |
| 338 | + else: |
| 339 | + current[path] = path_update |
| 340 | + return {**current, **update} |
| 341 | + |
| 342 | + |
| 343 | +def _dumps_with_fallback(obj: Any) -> str: |
| 344 | + try: |
| 345 | + return json.dumps(obj) |
| 346 | + except Exception: |
| 347 | + return "unserializable" |
0 commit comments