@@ -345,11 +345,7 @@ def test_async_parallel_notebooks(capfd, tmpdir):
345345 res = notebook_resources ()
346346
347347 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" )]
353349
354350 captured = capfd .readouterr ()
355351 assert filter_messages_on_error_output (captured .err ) == ""
@@ -370,9 +366,7 @@ def test_many_async_parallel_notebooks(capfd):
370366 # run once, to trigger creating the original context
371367 run_notebook (input_file , opts , res )
372368
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 )]
376370
377371 captured = capfd .readouterr ()
378372 assert filter_messages_on_error_output (captured .err ) == ""
@@ -741,6 +735,80 @@ def test_widgets(self):
741735 assert 'version_major' in wdata
742736 assert 'version_minor' in wdata
743737
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+
744812
745813class TestRunCell (NBClientTestsBase ):
746814 """Contains test functions for NotebookClient.execute_cell"""
@@ -1524,3 +1592,81 @@ def test_no_source(self, executor, cell_mock, message_mock):
15241592 assert message_mock .call_count == 0
15251593 # Should also consume the message stream
15261594 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