diff --git a/CHANGELOG.md b/CHANGELOG.md index ff3126734..599f8b04f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,15 +53,15 @@ Note the changelog here is not correct, presumably due to the recent branch rena - Fix expected text depending on IPython version. [#1354](https://github.com/ipython/ipykernel/pull/1354) ([@Carreau](https://github.com/Carreau)) - Another try at tracking down ResourceWarning with tracemalloc. [#1353](https://github.com/ipython/ipykernel/pull/1353) ([@Carreau](https://github.com/Carreau)) - Remove deprecated modules since 4.3 (2016). [#1352](https://github.com/ipython/ipykernel/pull/1352) ([@Carreau](https://github.com/Carreau)) -- Try to reenable tests from downstream ipywidgets [#1350](https://github.com/ipython/ipykernel/pull/1350) ([@Carreau](https://github.com/Carreau)) +- Try to re-enable tests from downstream ipywidgets [#1350](https://github.com/ipython/ipykernel/pull/1350) ([@Carreau](https://github.com/Carreau)) - Disable 3 failing downstream tests, but keep testing the rest. [#1349](https://github.com/ipython/ipykernel/pull/1349) ([@Carreau](https://github.com/Carreau)) -- Licence :: * trove classifers are deprecated [#1348](https://github.com/ipython/ipykernel/pull/1348) ([@Carreau](https://github.com/Carreau)) +- Licence :: * trove classifiers are deprecated [#1348](https://github.com/ipython/ipykernel/pull/1348) ([@Carreau](https://github.com/Carreau)) - Pin sphinx to resolve docs build failures [#1347](https://github.com/ipython/ipykernel/pull/1347) ([@krassowski](https://github.com/krassowski)) - Make our own mock kernel methods async [#1346](https://github.com/ipython/ipykernel/pull/1346) ([@Carreau](https://github.com/Carreau)) - Try to debug non-closed iopub socket [#1345](https://github.com/ipython/ipykernel/pull/1345) ([@Carreau](https://github.com/Carreau)) - Email is @python.org since 2018 [#1343](https://github.com/ipython/ipykernel/pull/1343) ([@Carreau](https://github.com/Carreau)) - Remove unused ignores lints. [#1342](https://github.com/ipython/ipykernel/pull/1342) ([@Carreau](https://github.com/Carreau)) -- Enable ruff G002 and fix 6 occurences [#1341](https://github.com/ipython/ipykernel/pull/1341) ([@Carreau](https://github.com/Carreau)) +- Enable ruff G002 and fix 6 occurrences [#1341](https://github.com/ipython/ipykernel/pull/1341) ([@Carreau](https://github.com/Carreau)) - Check ignores warnings are still relevant. [#1340](https://github.com/ipython/ipykernel/pull/1340) ([@Carreau](https://github.com/Carreau)) - Move mypy disablinging error codes on a per-file basis [#1338](https://github.com/ipython/ipykernel/pull/1338) ([@Carreau](https://github.com/Carreau)) - try to fix spyder kernel install [#1337](https://github.com/ipython/ipykernel/pull/1337) ([@Carreau](https://github.com/Carreau)) @@ -70,7 +70,7 @@ Note the changelog here is not correct, presumably due to the recent branch rena - Bump mypy [#1333](https://github.com/ipython/ipykernel/pull/1333) ([@Carreau](https://github.com/Carreau)) - Remove dead code. [#1332](https://github.com/ipython/ipykernel/pull/1332) ([@Carreau](https://github.com/Carreau)) - Ignore or fix most of the remaining ruff 0.9.6 errors [#1331](https://github.com/ipython/ipykernel/pull/1331) ([@Carreau](https://github.com/Carreau)) -- minor code reformating valid ruff 0.9.6 [#1330](https://github.com/ipython/ipykernel/pull/1330) ([@Carreau](https://github.com/Carreau)) +- minor code reformatting valid ruff 0.9.6 [#1330](https://github.com/ipython/ipykernel/pull/1330) ([@Carreau](https://github.com/Carreau)) - Some formatting changes to prepare bumping ruff pre-commit. [#1329](https://github.com/ipython/ipykernel/pull/1329) ([@Carreau](https://github.com/Carreau)) - Manually update Codespell and fix new errors. [#1328](https://github.com/ipython/ipykernel/pull/1328) ([@Carreau](https://github.com/Carreau)) - Manually update mdformat pre-commit and run it. [#1327](https://github.com/ipython/ipykernel/pull/1327) ([@Carreau](https://github.com/Carreau)) diff --git a/tests/test_subshells.py b/tests/test_subshells.py index 62394a0e6..4d906d44b 100644 --- a/tests/test_subshells.py +++ b/tests/test_subshells.py @@ -7,11 +7,20 @@ import platform import time from collections import Counter +from queue import Empty import pytest from jupyter_client.blocking.client import BlockingKernelClient -from .utils import TIMEOUT, assemble_output, get_replies, get_reply, new_kernel, wait_for_idle +from .utils import ( + TIMEOUT, + assemble_output, + flush_channels, + get_replies, + get_reply, + new_kernel, + wait_for_idle, +) # Helpers @@ -40,8 +49,10 @@ def list_subshell_helper(kc: BlockingKernelClient): return reply["content"] -def execute_request(kc: BlockingKernelClient, code: str, subshell_id: str | None): - msg = kc.session.msg("execute_request", {"code": code}) +def execute_request( + kc: BlockingKernelClient, code: str, subshell_id: str | None, silent: bool = False +): + msg = kc.session.msg("execute_request", {"code": code, "silent": silent}) msg["header"]["subshell_id"] = subshell_id kc.shell_channel.send(msg) return msg @@ -224,16 +235,16 @@ def test_execute_stop_on_error(are_subshells): msg = execute_request( kc, "import asyncio; await asyncio.sleep(1); raise ValueError()", subshell_ids[0] ) - msg_ids.append(msg["msg_id"]) + msg_ids.append(msg["header"]["msg_id"]) msg = execute_request(kc, "print('hello')", subshell_ids[0]) - msg_ids.append(msg["msg_id"]) + msg_ids.append(msg["header"]["msg_id"]) msg = execute_request(kc, "print('goodbye')", subshell_ids[0]) - msg_ids.append(msg["msg_id"]) + msg_ids.append(msg["header"]["msg_id"]) msg = execute_request(kc, "import time; time.sleep(1.5)", subshell_ids[1]) - msg_ids.append(msg["msg_id"]) + msg_ids.append(msg["header"]["msg_id"]) msg = execute_request(kc, "print('other')", subshell_ids[1]) - msg_ids.append(msg["msg_id"]) + msg_ids.append(msg["header"]["msg_id"]) replies = get_replies(kc, msg_ids) @@ -313,3 +324,70 @@ def test_idle_message_parent_headers(are_subshells): for subshell_id in subshell_ids: if subshell_id: delete_subshell_helper(kc, subshell_id) + + +def test_silent_flag_in_subshells(): + """Verifies that the 'silent' flag suppresses output in main and subshell contexts.""" + with new_kernel() as kc: + subshell_id = None + try: + flush_channels(kc) + # Test silent execution in main shell + msg_main_silent = execute_request(kc, "a=1", None, silent=True) + reply_main_silent = get_reply(kc, msg_main_silent["header"]["msg_id"]) + assert reply_main_silent["content"]["status"] == "ok" + + # Test silent execution in subshell + subshell_id = create_subshell_helper(kc)["subshell_id"] + msg_sub_silent = execute_request(kc, "b=2", subshell_id, silent=True) + reply_sub_silent = get_reply(kc, msg_sub_silent["header"]["msg_id"]) + assert reply_sub_silent["content"]["status"] == "ok" + + # Check for no iopub messages (other than status) from the silent requests + for msg_id in [msg_main_silent["header"]["msg_id"], msg_sub_silent["header"]["msg_id"]]: + while True: + try: + msg = kc.get_iopub_msg(timeout=0.2) + if msg["header"]["msg_type"] == "status": + continue + pytest.fail( + f"Silent execution produced unexpected IOPub message: {msg['header']['msg_type']}" + ) + except Empty: + break + + # Test concurrent silent and non-silent execution + msg_silent = execute_request( + kc, "import time; time.sleep(0.5); c=3", subshell_id, silent=True + ) + msg_noisy = execute_request(kc, "print('noisy')", None, silent=False) + + # Wait for both replies + get_replies(kc, [msg_silent["header"]["msg_id"], msg_noisy["header"]["msg_id"]]) + + # Verify that we only receive stream output from the noisy message + stdout, stderr = assemble_output( + kc.get_iopub_msg, parent_msg_id=msg_noisy["header"]["msg_id"] + ) + assert "noisy" in stdout + assert not stderr + + # Verify there is no output from the concurrent silent message + while True: + try: + msg = kc.get_iopub_msg(timeout=0.2) + if ( + msg["header"]["msg_type"] == "status" + and msg["parent_header"].get("msg_id") == msg_silent["header"]["msg_id"] + ): + continue + if msg["parent_header"].get("msg_id") == msg_silent["header"]["msg_id"]: + pytest.fail( + "Silent execution in concurrent setting produced unexpected IOPub message" + ) + except Empty: + break + finally: + # Ensure subshell is always deleted + if subshell_id: + delete_subshell_helper(kc, subshell_id)