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