Skip to content

Commit bbf6647

Browse files
committed
Tests: Add comprehensive tests for silent flag in subshells
1 parent 84211d7 commit bbf6647

File tree

1 file changed

+66
-11
lines changed

1 file changed

+66
-11
lines changed

tests/test_subshells.py

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66

77
import platform
88
import time
9+
from queue import Empty
910

1011
import pytest
1112
from jupyter_client.blocking.client import BlockingKernelClient
1213

13-
from .utils import TIMEOUT, assemble_output, get_replies, get_reply, new_kernel
14+
from .utils import TIMEOUT, assemble_output, get_replies, get_reply, new_kernel, flush_channels
1415

1516
# Helpers
1617

@@ -39,8 +40,8 @@ def list_subshell_helper(kc: BlockingKernelClient):
3940
return reply["content"]
4041

4142

42-
def execute_request(kc: BlockingKernelClient, code: str, subshell_id: str | None):
43-
msg = kc.session.msg("execute_request", {"code": code})
43+
def execute_request(kc: BlockingKernelClient, code: str, subshell_id: str | None, silent: bool = False):
44+
msg = kc.session.msg("execute_request", {"code": code, "silent": silent})
4445
msg["header"]["subshell_id"] = subshell_id
4546
kc.shell_channel.send(msg)
4647
return msg
@@ -51,17 +52,17 @@ def execute_request_subshell_id(
5152
):
5253
msg = execute_request(kc, code, subshell_id)
5354
msg_id = msg["header"]["msg_id"]
54-
stdout, _ = assemble_output(kc.get_iopub_msg, None, msg_id)
55+
stdout, _ = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_id)
5556
return stdout.strip()
5657

5758

5859
def execute_thread_count(kc: BlockingKernelClient) -> int:
59-
code = "print(threading.active_count())"
60+
code = "import threading; print(threading.active_count())"
6061
return int(execute_request_subshell_id(kc, code, None))
6162

6263

6364
def execute_thread_ids(kc: BlockingKernelClient, subshell_id: str | None = None) -> tuple[str, str]:
64-
code = "print(threading.get_ident(), threading.main_thread().ident)"
65+
code = "import threading; print(threading.get_ident(), threading.main_thread().ident)"
6566
return execute_request_subshell_id(kc, code, subshell_id).split()
6667

6768

@@ -223,16 +224,16 @@ def test_execute_stop_on_error(are_subshells):
223224
msg = execute_request(
224225
kc, "import asyncio; await asyncio.sleep(1); raise ValueError()", subshell_ids[0]
225226
)
226-
msg_ids.append(msg["msg_id"])
227+
msg_ids.append(msg["header"]["msg_id"])
227228
msg = execute_request(kc, "print('hello')", subshell_ids[0])
228-
msg_ids.append(msg["msg_id"])
229+
msg_ids.append(msg["header"]["msg_id"])
229230
msg = execute_request(kc, "print('goodbye')", subshell_ids[0])
230-
msg_ids.append(msg["msg_id"])
231+
msg_ids.append(msg["header"]["msg_id"])
231232

232233
msg = execute_request(kc, "import time; time.sleep(1.5)", subshell_ids[1])
233-
msg_ids.append(msg["msg_id"])
234+
msg_ids.append(msg["header"]["msg_id"])
234235
msg = execute_request(kc, "print('other')", subshell_ids[1])
235-
msg_ids.append(msg["msg_id"])
236+
msg_ids.append(msg["header"]["msg_id"])
236237

237238
replies = get_replies(kc, msg_ids)
238239

@@ -258,3 +259,57 @@ def test_execute_stop_on_error(are_subshells):
258259
for subshell_id in subshell_ids:
259260
if subshell_id:
260261
delete_subshell_helper(kc, subshell_id)
262+
263+
def test_silent_flag_in_subshells():
264+
"""Verifies that the 'silent' flag suppresses output in main and subshell contexts."""
265+
with new_kernel() as kc:
266+
flush_channels(kc)
267+
# Test silent execution in main shell
268+
msg_main_silent = execute_request(kc, "a=1", None, silent=True)
269+
reply_main_silent = get_reply(kc, msg_main_silent["header"]["msg_id"])
270+
assert reply_main_silent['content']['status'] == 'ok'
271+
272+
# Test silent execution in subshell
273+
subshell_id = create_subshell_helper(kc)["subshell_id"]
274+
msg_sub_silent = execute_request(kc, "b=2", subshell_id, silent=True)
275+
reply_sub_silent = get_reply(kc, msg_sub_silent["header"]["msg_id"])
276+
assert reply_sub_silent['content']['status'] == 'ok'
277+
278+
# Check for no output from silent requests. We should only see status messages,
279+
# so we expect a timeout here when looking for other messages.
280+
while True:
281+
try:
282+
msg = kc.get_iopub_msg(timeout=0.2)
283+
# We should only receive status messages
284+
if msg['header']['msg_type'] == 'status':
285+
continue
286+
# If we get anything else, it's a failure
287+
pytest.fail(f"Silent execution produced an unexpected IOPub message: {msg['header']['msg_type']}")
288+
except Empty:
289+
# No more messages, which is the expected behavior for silent execution
290+
break
291+
292+
# Test concurrent silent and non-silent execution
293+
msg_silent = execute_request(kc, "import time; time.sleep(0.5); c=3", subshell_id, silent=True)
294+
msg_noisy = execute_request(kc, "print('noisy')", None, silent=False)
295+
296+
# Get replies for both messages
297+
replies = get_replies(kc, [msg_silent['header']['msg_id'], msg_noisy['header']['msg_id']])
298+
assert len(replies) == 2
299+
300+
# Verify that we only receive stream output from the noisy message
301+
stdout, stderr = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_noisy['header']['msg_id'])
302+
assert "noisy" in stdout
303+
assert not stderr
304+
305+
# Verify there is no output from the silent message.
306+
try:
307+
stdout, stderr = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_silent['header']['msg_id'])
308+
assert not stdout
309+
assert not stderr
310+
except Empty:
311+
# This is expected since there should be no output
312+
pass
313+
314+
# Cleanup
315+
delete_subshell_helper(kc, subshell_id)

0 commit comments

Comments
 (0)