Skip to content

Commit cc7a196

Browse files
jeremymanningclaude
andcommitted
Fix notebook magic tests for environments without IPython/ipywidgets
- Add comprehensive placeholder classes and functions when IPython unavailable - Include display, HTML, and complete widgets module mock classes - Update test fixtures to use setattr() instead of patching non-existent attributes - Fix magic method tests to work regardless of IPython availability in test environment - Remove redundant display() patching that was causing AttributeError - All 279 tests now pass including previously failing notebook magic tests Resolves GitHub Actions test failures in environments with different IPython configurations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 5912494 commit cc7a196

File tree

2 files changed

+79
-23
lines changed

2 files changed

+79
-23
lines changed

clustrix/notebook_magic.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,66 @@ def magics_class(cls):
3030

3131
def cell_magic(name):
3232
def decorator(func):
33+
# When IPython isn't available, return the original function unchanged
3334
return func
3435

3536
return decorator
3637

38+
def display(*args, **kwargs):
39+
"""Placeholder display function."""
40+
pass
41+
42+
class HTML: # type: ignore
43+
"""Placeholder HTML class."""
44+
45+
def __init__(self, *args, **kwargs):
46+
pass
47+
48+
# Mock widgets module
49+
class widgets: # type: ignore
50+
class Dropdown:
51+
def __init__(self, *args, **kwargs):
52+
pass
53+
54+
class Button:
55+
def __init__(self, *args, **kwargs):
56+
pass
57+
58+
class Text:
59+
def __init__(self, *args, **kwargs):
60+
pass
61+
62+
class IntText:
63+
def __init__(self, *args, **kwargs):
64+
pass
65+
66+
class Textarea:
67+
def __init__(self, *args, **kwargs):
68+
pass
69+
70+
class Output:
71+
def __init__(self, *args, **kwargs):
72+
pass
73+
74+
def clear_output(self, *args, **kwargs):
75+
pass
76+
77+
class VBox:
78+
def __init__(self, *args, **kwargs):
79+
pass
80+
81+
class HBox:
82+
def __init__(self, *args, **kwargs):
83+
pass
84+
85+
class HTML:
86+
def __init__(self, *args, **kwargs):
87+
pass
88+
89+
class Layout:
90+
def __init__(self, *args, **kwargs):
91+
pass
92+
3793

3894
from .config import configure, get_config
3995

tests/test_notebook_magic.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ def mock_ipython(self):
8787
# Set the widgets attribute on the module
8888
setattr(clustrix.notebook_magic, "widgets", mock_widgets)
8989

90-
with patch("clustrix.notebook_magic.display") as mock_display:
91-
mock_display.return_value = None
92-
yield
90+
# Mock display function
91+
mock_display = MagicMock()
92+
setattr(clustrix.notebook_magic, "display", mock_display)
93+
94+
yield
9395

9496
def test_widget_initialization(self, mock_ipython):
9597
"""Test widget initialization with defaults."""
@@ -364,11 +366,11 @@ class TestClusterfyMagics:
364366

365367
def test_magic_without_ipython(self):
366368
"""Test magic command fails gracefully without IPython."""
369+
# Patch the IPYTHON_AVAILABLE check directly in the method
367370
with patch("clustrix.notebook_magic.IPYTHON_AVAILABLE", False):
368-
# Create a simple mock without trying to use the Magics base class
369-
mock_shell = MagicMock()
370-
magic = ClusterfyMagics.__new__(ClusterfyMagics) # Create without __init__
371-
magic.shell = mock_shell
371+
# Create magic instance properly
372+
magic = ClusterfyMagics()
373+
magic.shell = MagicMock()
372374

373375
# Should print error message
374376
with patch("builtins.print") as mock_print:
@@ -382,31 +384,29 @@ def test_magic_with_ipython(self):
382384
"""Test magic command creates widget with IPython."""
383385
with patch("clustrix.notebook_magic.IPYTHON_AVAILABLE", True):
384386
with patch("clustrix.notebook_magic.ClusterConfigWidget") as MockWidget:
385-
with patch("clustrix.notebook_magic.display"):
386-
magic = ClusterfyMagics()
387-
magic.shell = MagicMock()
387+
magic = ClusterfyMagics()
388+
magic.shell = MagicMock()
388389

389-
# Call magic
390-
magic.clusterfy("", "")
390+
# Call magic
391+
magic.clusterfy("", "")
391392

392-
# Check widget was created and displayed
393-
MockWidget.assert_called_once()
394-
MockWidget.return_value.display.assert_called_once()
393+
# Check widget was created and displayed
394+
MockWidget.assert_called_once()
395+
MockWidget.return_value.display.assert_called_once()
395396

396397
def test_magic_executes_cell_code(self):
397398
"""Test magic command executes code in cell."""
398399
with patch("clustrix.notebook_magic.IPYTHON_AVAILABLE", True):
399400
with patch("clustrix.notebook_magic.ClusterConfigWidget"):
400-
with patch("clustrix.notebook_magic.display"):
401-
magic = ClusterfyMagics()
402-
magic.shell = MagicMock()
401+
magic = ClusterfyMagics()
402+
magic.shell = MagicMock()
403403

404-
# Call magic with code
405-
test_code = "x = 42"
406-
magic.clusterfy("", test_code)
404+
# Call magic with code
405+
test_code = "x = 42"
406+
magic.clusterfy("", test_code)
407407

408-
# Check code was executed
409-
magic.shell.run_cell.assert_called_once_with(test_code)
408+
# Check code was executed
409+
magic.shell.run_cell.assert_called_once_with(test_code)
410410

411411

412412
class TestIPythonExtension:

0 commit comments

Comments
 (0)