|
6 | 6 |
|
7 | 7 | import platform |
8 | 8 | import time |
| 9 | +from collections import Counter |
9 | 10 | from queue import Empty |
10 | 11 |
|
11 | 12 | import pytest |
12 | 13 | from jupyter_client.blocking.client import BlockingKernelClient |
13 | 14 |
|
14 | | -from .utils import TIMEOUT, assemble_output, flush_channels, get_replies, get_reply, new_kernel |
| 15 | +from .utils import ( |
| 16 | + TIMEOUT, |
| 17 | + assemble_output, |
| 18 | + flush_channels, |
| 19 | + get_replies, |
| 20 | + get_reply, |
| 21 | + new_kernel, |
| 22 | + wait_for_idle, |
| 23 | +) |
15 | 24 |
|
16 | 25 | # Helpers |
17 | 26 |
|
@@ -54,17 +63,17 @@ def execute_request_subshell_id( |
54 | 63 | ): |
55 | 64 | msg = execute_request(kc, code, subshell_id) |
56 | 65 | msg_id = msg["header"]["msg_id"] |
57 | | - stdout, _ = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_id) |
| 66 | + stdout, _ = assemble_output(kc.get_iopub_msg, None, msg_id) |
58 | 67 | return stdout.strip() |
59 | 68 |
|
60 | 69 |
|
61 | 70 | def execute_thread_count(kc: BlockingKernelClient) -> int: |
62 | | - code = "import threading; print(threading.active_count())" |
| 71 | + code = "print(threading.active_count())" |
63 | 72 | return int(execute_request_subshell_id(kc, code, None)) |
64 | 73 |
|
65 | 74 |
|
66 | 75 | def execute_thread_ids(kc: BlockingKernelClient, subshell_id: str | None = None) -> tuple[str, str]: |
67 | | - code = "import threading; print(threading.get_ident(), threading.main_thread().ident)" |
| 76 | + code = "print(threading.get_ident(), threading.main_thread().ident)" |
68 | 77 | return execute_request_subshell_id(kc, code, subshell_id).split() |
69 | 78 |
|
70 | 79 |
|
@@ -263,6 +272,60 @@ def test_execute_stop_on_error(are_subshells): |
263 | 272 | delete_subshell_helper(kc, subshell_id) |
264 | 273 |
|
265 | 274 |
|
| 275 | +@pytest.mark.parametrize("are_subshells", [(False, True), (True, False), (True, True)]) |
| 276 | +def test_idle_message_parent_headers(are_subshells): |
| 277 | + with new_kernel() as kc: |
| 278 | + # import time module on main shell. |
| 279 | + msg = kc.session.msg("execute_request", {"code": "import time"}) |
| 280 | + kc.shell_channel.send(msg) |
| 281 | + |
| 282 | + subshell_ids = [ |
| 283 | + create_subshell_helper(kc)["subshell_id"] if is_subshell else None |
| 284 | + for is_subshell in are_subshells |
| 285 | + ] |
| 286 | + |
| 287 | + # Wait for all idle status messages to be received. |
| 288 | + for _ in range(1 + sum(are_subshells)): |
| 289 | + wait_for_idle(kc) |
| 290 | + |
| 291 | + msg_ids = [] |
| 292 | + for subshell_id in subshell_ids: |
| 293 | + msg = execute_request(kc, "time.sleep(0.5)", subshell_id) |
| 294 | + msg_ids.append(msg["msg_id"]) |
| 295 | + |
| 296 | + # Expect 4 status messages (2 busy, 2 idle) on iopub channel for the two execute_requests |
| 297 | + statuses = [] |
| 298 | + timeout = TIMEOUT # Combined timeout to receive all the status messages |
| 299 | + t0 = time.time() |
| 300 | + while True: |
| 301 | + status = kc.get_iopub_msg(timeout=timeout) |
| 302 | + if status["msg_type"] != "status" or status["parent_header"]["msg_id"] not in msg_ids: |
| 303 | + continue |
| 304 | + statuses.append(status) |
| 305 | + if len(statuses) == 4: |
| 306 | + break |
| 307 | + t1 = time.time() |
| 308 | + timeout -= t1 - t0 |
| 309 | + t0 = t1 |
| 310 | + |
| 311 | + execution_states = Counter(msg["content"]["execution_state"] for msg in statuses) |
| 312 | + assert execution_states["busy"] == 2 |
| 313 | + assert execution_states["idle"] == 2 |
| 314 | + |
| 315 | + parent_msg_ids = Counter(msg["parent_header"]["msg_id"] for msg in statuses) |
| 316 | + assert parent_msg_ids[msg_ids[0]] == 2 |
| 317 | + assert parent_msg_ids[msg_ids[1]] == 2 |
| 318 | + |
| 319 | + parent_subshell_ids = Counter(msg["parent_header"].get("subshell_id") for msg in statuses) |
| 320 | + assert parent_subshell_ids[subshell_ids[0]] == 2 |
| 321 | + assert parent_subshell_ids[subshell_ids[1]] == 2 |
| 322 | + |
| 323 | + # Cleanup |
| 324 | + for subshell_id in subshell_ids: |
| 325 | + if subshell_id: |
| 326 | + delete_subshell_helper(kc, subshell_id) |
| 327 | + |
| 328 | + |
266 | 329 | def test_silent_flag_in_subshells(): |
267 | 330 | """Verifies that the 'silent' flag suppresses output in main and subshell contexts.""" |
268 | 331 | with new_kernel() as kc: |
|
0 commit comments