Skip to content

Commit 54b3817

Browse files
committed
test: fix IPython import mocking in Jupyter stderr tests
- Mock builtins.__import__ instead of patching IPython.display directly - Handle 'from IPython.display import HTML, display' import pattern - Tests now work without IPython installed in test environment
1 parent 9fc1d36 commit 54b3817

File tree

1 file changed

+51
-5
lines changed

1 file changed

+51
-5
lines changed

tests/client/test_stdio.py

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -726,22 +726,68 @@ def test_print_stderr_non_jupyter():
726726
def test_print_stderr_jupyter():
727727
"""Test stderr printing when in Jupyter using IPython display."""
728728
# Mock the Jupyter detection and IPython display
729+
# We need to mock the import inside the function since IPython may not be installed
730+
mock_html_class = MagicMock()
731+
mock_display_func = MagicMock()
732+
733+
# Create a mock module structure that matches "from IPython.display import HTML, display"
734+
mock_display_module = MagicMock()
735+
mock_display_module.HTML = mock_html_class
736+
mock_display_module.display = mock_display_func
737+
738+
# Create mock IPython module with display submodule
739+
mock_ipython_module = MagicMock()
740+
mock_ipython_module.display = mock_display_module
741+
742+
original_import = __import__
743+
744+
def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
745+
if name == "IPython.display":
746+
return mock_display_module
747+
if name == "IPython":
748+
return mock_ipython_module
749+
# For other imports, use real import
750+
return original_import(name, globals, locals, fromlist, level)
751+
729752
with patch("mcp.client.stdio._is_jupyter_notebook", return_value=True), patch(
730-
"IPython.display.display"
731-
) as mock_display, patch("IPython.display.HTML") as mock_html:
753+
"builtins.__import__", side_effect=mock_import
754+
):
732755
_print_stderr("test error message", sys.stderr)
733756

734757
# Verify IPython display was called
735-
mock_html.assert_called_once()
736-
mock_display.assert_called_once()
758+
mock_html_class.assert_called_once()
759+
mock_display_func.assert_called_once()
737760

738761

739762
def test_print_stderr_jupyter_fallback():
740763
"""Test stderr printing falls back to regular print if IPython display fails."""
741764
stderr_capture = io.StringIO()
765+
766+
# Mock IPython import to raise exception on display
767+
mock_html_class = MagicMock()
768+
mock_display_func = MagicMock(side_effect=Exception("Display failed"))
769+
770+
# Create a mock module structure that matches "from IPython.display import HTML, display"
771+
mock_display_module = MagicMock()
772+
mock_display_module.HTML = mock_html_class
773+
mock_display_module.display = mock_display_func
774+
775+
# Create mock IPython module with display submodule
776+
mock_ipython_module = MagicMock()
777+
mock_ipython_module.display = mock_display_module
778+
779+
original_import = __import__
780+
781+
def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
782+
if name == "IPython.display":
783+
return mock_display_module
784+
if name == "IPython":
785+
return mock_ipython_module
786+
# For other imports, use real import
787+
return original_import(name, globals, locals, fromlist, level)
742788

743789
with patch("mcp.client.stdio._is_jupyter_notebook", return_value=True), patch(
744-
"IPython.display.display", side_effect=Exception("Display failed")
790+
"builtins.__import__", side_effect=mock_import
745791
):
746792
_print_stderr("test error message", stderr_capture)
747793

0 commit comments

Comments
 (0)