@@ -737,6 +737,45 @@ def test_widgets(self):
737
737
assert 'version_major' in wdata
738
738
assert 'version_minor' in wdata
739
739
740
+ def test_execution_hook (self ):
741
+ filename = os .path .join (current_dir , 'files' , 'HelloWorld.ipynb' )
742
+ with open (filename ) as f :
743
+ input_nb = nbformat .read (f , 4 )
744
+ cell_hook = MagicMock ()
745
+ execution_hook = MagicMock ()
746
+
747
+ executor = NotebookClient (
748
+ input_nb ,
749
+ resources = NBClientTestsBase ().build_resources (),
750
+ on_cell_start = cell_hook ,
751
+ on_cell_complete = cell_hook ,
752
+ on_cell_error = cell_hook ,
753
+ on_execution_start = execution_hook ,
754
+ )
755
+ executor .execute ()
756
+ execution_hook .assert_called_once ()
757
+ assert cell_hook .call_count == 2
758
+
759
+ def test_error_execution_hook_error (self ):
760
+ filename = os .path .join (current_dir , 'files' , 'Error.ipynb' )
761
+ with open (filename ) as f :
762
+ input_nb = nbformat .read (f , 4 )
763
+ cell_hook = MagicMock ()
764
+ execution_hook = MagicMock ()
765
+
766
+ executor = NotebookClient (
767
+ input_nb ,
768
+ resources = NBClientTestsBase ().build_resources (),
769
+ on_cell_start = cell_hook ,
770
+ on_cell_complete = cell_hook ,
771
+ on_cell_error = cell_hook ,
772
+ on_execution_start = execution_hook ,
773
+ )
774
+ with pytest .raises (CellExecutionError ):
775
+ executor .execute ()
776
+ execution_hook .assert_called_once ()
777
+ assert cell_hook .call_count == 3
778
+
740
779
741
780
class TestRunCell (NBClientTestsBase ):
742
781
"""Contains test functions for NotebookClient.execute_cell"""
@@ -1520,3 +1559,51 @@ def test_no_source(self, executor, cell_mock, message_mock):
1520
1559
assert message_mock .call_count == 0
1521
1560
# Should also consume the message stream
1522
1561
assert cell_mock .outputs == []
1562
+
1563
+ @prepare_cell_mocks ()
1564
+ async def test_cell_hooks (self , executor , cell_mock , message_mock ):
1565
+ hook1 , hook2 , hook3 , hook4 = MagicMock (), MagicMock (), MagicMock (), MagicMock ()
1566
+ tasks = [hook1 , hook2 , hook3 , hook4 ]
1567
+ executor .on_cell_start = hook1
1568
+ executor .on_cell_complete = hook2
1569
+ executor .on_cell_error = hook3
1570
+ executor .on_execution_start = hook4
1571
+ executor .execute_cell (cell_mock , 0 )
1572
+ await asyncio .gather (* tasks )
1573
+ assert hook1 .call_count == 1
1574
+ assert hook2 .call_count == 1
1575
+ assert hook3 .call_count == 0
1576
+ assert hook4 .call_count == 0
1577
+ hook1 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1578
+ hook2 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1579
+
1580
+ @prepare_cell_mocks (
1581
+ {
1582
+ 'msg_type' : 'error' ,
1583
+ 'header' : {'msg_type' : 'error' },
1584
+ 'content' : {'ename' : 'foo' , 'evalue' : 'bar' , 'traceback' : ['Boom' ]},
1585
+ },
1586
+ reply_msg = {
1587
+ 'msg_type' : 'execute_reply' ,
1588
+ 'header' : {'msg_type' : 'execute_reply' },
1589
+ # ERROR
1590
+ 'content' : {'status' : 'error' },
1591
+ },
1592
+ )
1593
+ async def test_error_cell_hooks (self , executor , cell_mock , message_mock ):
1594
+ hook1 , hook2 , hook3 , hook4 = MagicMock (), MagicMock (), MagicMock (), MagicMock ()
1595
+ tasks = [hook1 , hook2 , hook3 , hook4 ]
1596
+ executor .on_cell_start = hook1
1597
+ executor .on_cell_complete = hook2
1598
+ executor .on_cell_error = hook3
1599
+ executor .on_execution_start = hook4
1600
+ with self .assertRaises (CellExecutionError ):
1601
+ executor .execute_cell (cell_mock , 0 )
1602
+ await asyncio .gather (* tasks )
1603
+ assert hook1 .call_count == 1
1604
+ assert hook2 .call_count == 1
1605
+ assert hook3 .call_count == 1
1606
+ assert hook4 .call_count == 0
1607
+ hook1 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1608
+ hook2 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1609
+ hook3 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
0 commit comments