@@ -345,11 +345,7 @@ def test_async_parallel_notebooks(capfd, tmpdir):
345
345
res = notebook_resources ()
346
346
347
347
with modified_env ({"NBEXECUTE_TEST_PARALLEL_TMPDIR" : str (tmpdir )}):
348
- tasks = [
349
- async_run_notebook (input_file .format (label = label ), opts , res ) for label in ("A" , "B" )
350
- ]
351
- loop = asyncio .get_event_loop ()
352
- loop .run_until_complete (asyncio .gather (* tasks ))
348
+ [async_run_notebook (input_file .format (label = label ), opts , res ) for label in ("A" , "B" )]
353
349
354
350
captured = capfd .readouterr ()
355
351
assert filter_messages_on_error_output (captured .err ) == ""
@@ -370,9 +366,7 @@ def test_many_async_parallel_notebooks(capfd):
370
366
# run once, to trigger creating the original context
371
367
run_notebook (input_file , opts , res )
372
368
373
- tasks = [async_run_notebook (input_file , opts , res ) for i in range (4 )]
374
- loop = asyncio .get_event_loop ()
375
- loop .run_until_complete (asyncio .gather (* tasks ))
369
+ [async_run_notebook (input_file , opts , res ) for i in range (4 )]
376
370
377
371
captured = capfd .readouterr ()
378
372
assert filter_messages_on_error_output (captured .err ) == ""
@@ -741,6 +735,80 @@ def test_widgets(self):
741
735
assert 'version_major' in wdata
742
736
assert 'version_minor' in wdata
743
737
738
+ def test_execution_hook (self ):
739
+ filename = os .path .join (current_dir , 'files' , 'HelloWorld.ipynb' )
740
+ with open (filename ) as f :
741
+ input_nb = nbformat .read (f , 4 )
742
+ hook1 , hook2 , hook3 , hook4 = MagicMock (), MagicMock (), MagicMock (), MagicMock ()
743
+ executor = NotebookClient (
744
+ input_nb ,
745
+ on_cell_start = hook1 ,
746
+ on_cell_complete = hook2 ,
747
+ on_cell_error = hook3 ,
748
+ on_execution_start = hook4 ,
749
+ )
750
+ executor .execute ()
751
+ hook1 .assert_called_once ()
752
+ hook2 .assert_called_once ()
753
+ hook3 .assert_not_called ()
754
+ hook4 .assert_called_once ()
755
+
756
+ def test_error_execution_hook_error (self ):
757
+ filename = os .path .join (current_dir , 'files' , 'Error.ipynb' )
758
+ with open (filename ) as f :
759
+ input_nb = nbformat .read (f , 4 )
760
+ hook1 , hook2 , hook3 , hook4 = MagicMock (), MagicMock (), MagicMock (), MagicMock ()
761
+ executor = NotebookClient (
762
+ input_nb ,
763
+ on_cell_start = hook1 ,
764
+ on_cell_complete = hook2 ,
765
+ on_cell_error = hook3 ,
766
+ on_execution_start = hook4 ,
767
+ )
768
+ with pytest .raises (CellExecutionError ):
769
+ executor .execute ()
770
+ hook1 .assert_called_once ()
771
+ hook2 .assert_called_once ()
772
+ hook3 .assert_called_once ()
773
+ hook4 .assert_called_once ()
774
+
775
+ def test_async_execution_hook (self ):
776
+ filename = os .path .join (current_dir , 'files' , 'HelloWorld.ipynb' )
777
+ with open (filename ) as f :
778
+ input_nb = nbformat .read (f , 4 )
779
+ hook1 , hook2 , hook3 , hook4 = AsyncMock (), AsyncMock (), AsyncMock (), AsyncMock ()
780
+ executor = NotebookClient (
781
+ input_nb ,
782
+ on_cell_start = hook1 ,
783
+ on_cell_complete = hook2 ,
784
+ on_cell_error = hook3 ,
785
+ on_execution_start = hook4 ,
786
+ )
787
+ executor .execute ()
788
+ hook1 .assert_called_once ()
789
+ hook2 .assert_called_once ()
790
+ hook3 .assert_not_called ()
791
+ hook4 .assert_called_once ()
792
+
793
+ def test_error_async_execution_hook (self ):
794
+ filename = os .path .join (current_dir , 'files' , 'Error.ipynb' )
795
+ with open (filename ) as f :
796
+ input_nb = nbformat .read (f , 4 )
797
+ hook1 , hook2 , hook3 , hook4 = AsyncMock (), AsyncMock (), AsyncMock (), AsyncMock ()
798
+ executor = NotebookClient (
799
+ input_nb ,
800
+ on_cell_start = hook1 ,
801
+ on_cell_complete = hook2 ,
802
+ on_cell_error = hook3 ,
803
+ on_execution_start = hook4 ,
804
+ )
805
+ with pytest .raises (CellExecutionError ):
806
+ executor .execute ().execute ()
807
+ hook1 .assert_called_once ()
808
+ hook2 .assert_called_once ()
809
+ hook3 .assert_called_once ()
810
+ hook4 .assert_called_once ()
811
+
744
812
745
813
class TestRunCell (NBClientTestsBase ):
746
814
"""Contains test functions for NotebookClient.execute_cell"""
@@ -1524,3 +1592,81 @@ def test_no_source(self, executor, cell_mock, message_mock):
1524
1592
assert message_mock .call_count == 0
1525
1593
# Should also consume the message stream
1526
1594
assert cell_mock .outputs == []
1595
+
1596
+ @prepare_cell_mocks ()
1597
+ def test_cell_hooks (self , executor , cell_mock , message_mock ):
1598
+ hook1 , hook2 , hook3 , hook4 = MagicMock (), MagicMock (), MagicMock (), MagicMock ()
1599
+ executor .on_cell_start = hook1
1600
+ executor .on_cell_complete = hook2
1601
+ executor .on_cell_error = hook3
1602
+ executor .on_execution_start = hook4
1603
+ executor .execute_cell (cell_mock , 0 )
1604
+ hook1 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1605
+ hook2 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1606
+ hook3 .assert_not_called ()
1607
+ hook4 .assert_not_called ()
1608
+
1609
+ @prepare_cell_mocks (
1610
+ {
1611
+ 'msg_type' : 'error' ,
1612
+ 'header' : {'msg_type' : 'error' },
1613
+ 'content' : {'ename' : 'foo' , 'evalue' : 'bar' , 'traceback' : ['Boom' ]},
1614
+ },
1615
+ reply_msg = {
1616
+ 'msg_type' : 'execute_reply' ,
1617
+ 'header' : {'msg_type' : 'execute_reply' },
1618
+ # ERROR
1619
+ 'content' : {'status' : 'error' },
1620
+ },
1621
+ )
1622
+ def test_error_cell_hooks (self , executor , cell_mock , message_mock ):
1623
+ hook1 , hook2 , hook3 , hook4 = MagicMock (), MagicMock (), MagicMock (), MagicMock ()
1624
+ executor .on_cell_start = hook1
1625
+ executor .on_cell_complete = hook2
1626
+ executor .on_cell_error = hook3
1627
+ executor .on_execution_start = hook4
1628
+ with self .assertRaises (CellExecutionError ):
1629
+ executor .execute_cell (cell_mock , 0 )
1630
+ hook1 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1631
+ hook2 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1632
+ hook3 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1633
+ hook4 .assert_not_called ()
1634
+
1635
+ @prepare_cell_mocks ()
1636
+ def test_async_cell_hooks (self , executor , cell_mock , message_mock ):
1637
+ hook1 , hook2 , hook3 , hook4 = AsyncMock (), AsyncMock (), AsyncMock (), AsyncMock ()
1638
+ executor .on_cell_start = hook1
1639
+ executor .on_cell_complete = hook2
1640
+ executor .on_cell_error = hook3
1641
+ executor .on_execution_start = hook4
1642
+ executor .execute_cell (cell_mock , 0 )
1643
+ hook1 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1644
+ hook2 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1645
+ hook3 .assert_not_called ()
1646
+ hook4 .assert_not_called ()
1647
+
1648
+ @prepare_cell_mocks (
1649
+ {
1650
+ 'msg_type' : 'error' ,
1651
+ 'header' : {'msg_type' : 'error' },
1652
+ 'content' : {'ename' : 'foo' , 'evalue' : 'bar' , 'traceback' : ['Boom' ]},
1653
+ },
1654
+ reply_msg = {
1655
+ 'msg_type' : 'execute_reply' ,
1656
+ 'header' : {'msg_type' : 'execute_reply' },
1657
+ # ERROR
1658
+ 'content' : {'status' : 'error' },
1659
+ },
1660
+ )
1661
+ def test_error_async_cell_hooks (self , executor , cell_mock , message_mock ):
1662
+ hook1 , hook2 , hook3 , hook4 = AsyncMock (), AsyncMock (), AsyncMock (), AsyncMock ()
1663
+ executor .on_cell_start = hook1
1664
+ executor .on_cell_complete = hook2
1665
+ executor .on_cell_error = hook3
1666
+ executor .on_execution_start = hook4
1667
+ with self .assertRaises (CellExecutionError ):
1668
+ executor .execute_cell (cell_mock , 0 )
1669
+ hook1 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1670
+ hook2 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1671
+ hook3 .assert_called_once_with (cell = cell_mock , cell_index = 0 )
1672
+ hook4 .assert_not_called ()
0 commit comments