55
66import json
77import re
8- from typing import TYPE_CHECKING , Any
8+ from importlib .util import find_spec
9+ from typing import TYPE_CHECKING , Any , Literal , TypedDict
910
1011import pytest
1112from jupyter_client .manager import start_new_async_kernel
1819
1920 from jupyter_client .asynchronous .client import AsyncKernelClient
2021
21- Execute = Callable [[str ], Awaitable [list [dict [str , str ]]]]
22+ class Result (TypedDict ): # noqa: D101
23+ msg_type : Literal ["execute_result" ]
24+ data : dict [str , str ]
25+ metadata : dict [str , str ]
26+
27+ class Display (TypedDict ): # noqa: D101
28+ msg_type : Literal ["display_data" ]
29+ data : dict [str , str ]
30+ metadata : dict [str , str ]
31+
32+ class Stream (TypedDict ): # noqa: D101
33+ msg_type : Literal ["stream" ]
34+ name : str
35+ text : str
36+
37+ SimpleMsg = Result | Display | Stream
38+
39+ Execute = Callable [[str ], Awaitable [list ["SimpleMsg" ]]]
40+
41+
42+ HAS_IPYWIDGETS = bool (find_spec ("ipywidgets" ))
43+
44+
45+ def simple_msg (msg : dict [str , Any ]) -> SimpleMsg | None :
46+ if msg ["header" ]["msg_type" ] not in {"execute_result" , "display_data" , "stream" }:
47+ return None
48+ return dict (msg_type = msg ["header" ]["msg_type" ], ** msg ["content" ]) # type: ignore[return-value]
2249
2350
2451@pytest .fixture (scope = "session" )
@@ -33,7 +60,7 @@ async def kernel_client() -> AsyncGenerator[AsyncKernelClient, None]:
3360async def execute (
3461 kernel_client : AsyncKernelClient , libdir_test : Path
3562) -> AsyncGenerator [Execute , None ]:
36- async def execute (code : str ) -> list [dict [ str , str ] ]:
63+ async def execute (code : str ) -> list [SimpleMsg ]:
3764 await kernel_client .wait_for_ready ()
3865 msgs : list [dict [str , Any ]] = []
3966 reply = await kernel_client .execute_interactive ( # pyright: ignore[reportUnknownMemberType]
@@ -48,11 +75,7 @@ async def execute(code: str) -> list[dict[str, str]]:
4875 pytest .fail ("{ename}: {evalue}\n {traceback}" .format_map (content ))
4976 elif reply ["content" ]["status" ] != "ok" :
5077 pytest .fail (f"Unexpected reply status: { reply ['content' ]['status' ]} " )
51- return [
52- msg ["content" ]["data" ]
53- for msg in msgs
54- if msg ["header" ]["msg_type" ] == "execute_result"
55- ]
78+ return [smsg for msg in msgs if (smsg := simple_msg (msg )) is not None ]
5679
5780 await execute (
5881 f"""
@@ -100,15 +123,25 @@ async def execute(code: str) -> list[dict[str, str]]:
100123)
101124async def test_run (execute : Execute , code : str , expected : str ) -> None :
102125 await execute (code )
103- [mimebundle ] = await execute (RUN )
104- assert mimebundle .keys () == {
126+ [* msgs , result ] = await execute (RUN )
127+
128+ if HAS_IPYWIDGETS :
129+ assert not msgs
130+ else :
131+ [msg ] = msgs
132+ assert msg ["msg_type" ] == "stream"
133+ assert msg ["name" ] == "stderr"
134+ assert f"Failed to import dependencies for { MIME_WIDGET } " in msg ["text" ]
135+ assert "ModuleNotFoundError: No module named 'ipywidgets'" in msg ["text" ]
136+
137+ assert result ["msg_type" ] == "execute_result"
138+ assert set (result ["data" ].keys ()) == {
105139 "text/plain" ,
106140 "text/markdown" ,
107141 "text/html" ,
108- "application/json" ,
109- MIME_WIDGET ,
142+ * ([MIME_WIDGET ] if HAS_IPYWIDGETS else []),
110143 }
111- r = mimebundle ["text/plain" ]
144+ r = result [ "data" ] ["text/plain" ]
112145 pkgs , info = r .split ("\n ----\t ----\n " ) if "----" in r else ("" , r )
113146 assert pkgs == expected
114147 # No CPU info by default
0 commit comments