6
6
# contextlib, and we `await yield_()` instead of just `yield`
7
7
from async_generator import asynccontextmanager , async_generator , yield_
8
8
9
- import nest_asyncio
10
-
11
9
from time import monotonic
12
10
from queue import Empty
13
11
import asyncio
@@ -97,6 +95,21 @@ class NotebookClient(LoggingConfigurable):
97
95
),
98
96
).tag (config = True )
99
97
98
+ nest_asyncio = Bool (
99
+ False ,
100
+ help = dedent (
101
+ """
102
+ If False (default), then blocking functions such as `execute`
103
+ assume that no event loop is already running. These functions
104
+ run their async counterparts (e.g. `async_execute`) in an event
105
+ loop with `asyncio.run_until_complete`, which will fail if an
106
+ event loop is already running. This can be the case if nbclient
107
+ is used e.g. in a Jupyter Notebook. In that case, `nest_asyncio`
108
+ should be set to True.
109
+ """
110
+ ),
111
+ ).tag (config = True )
112
+
100
113
force_raise_errors = Bool (
101
114
False ,
102
115
help = dedent (
@@ -367,21 +380,9 @@ async def setup_kernel(self, **kwargs):
367
380
self .kc .stop_channels ()
368
381
self .kc = None
369
382
370
- def execute (self , ** kwargs ):
371
- """
372
- Executes each code cell (blocking).
373
-
374
- Returns
375
- -------
376
- nb : NotebookNode
377
- The executed notebook.
378
- """
379
- loop = get_loop ()
380
- return loop .run_until_complete (self .async_execute (** kwargs ))
381
-
382
383
async def async_execute (self , ** kwargs ):
383
384
"""
384
- Executes each code cell asynchronously .
385
+ Executes each code cell.
385
386
386
387
Returns
387
388
-------
@@ -550,48 +551,9 @@ def _check_raise_for_error(self, cell, exec_reply):
550
551
if (exec_reply is not None ) and exec_reply ['content' ]['status' ] == 'error' :
551
552
raise CellExecutionError .from_cell_and_msg (cell , exec_reply ['content' ])
552
553
553
- def execute_cell (self , cell , cell_index , execution_count = None , store_history = True ):
554
- """
555
- Executes a single code cell (blocking).
556
-
557
- To execute all cells see :meth:`execute`.
558
-
559
- Parameters
560
- ----------
561
- cell : nbformat.NotebookNode
562
- The cell which is currently being processed.
563
- cell_index : int
564
- The position of the cell within the notebook object.
565
- execution_count : int
566
- The execution count to be assigned to the cell (default: Use kernel response)
567
- store_history : bool
568
- Determines if history should be stored in the kernel (default: False).
569
- Specific to ipython kernels, which can store command histories.
570
-
571
- Returns
572
- -------
573
- output : dict
574
- The execution output payload (or None for no output).
575
-
576
- Raises
577
- ------
578
- CellExecutionError
579
- If execution failed and should raise an exception, this will be raised
580
- with defaults about the failure.
581
-
582
- Returns
583
- -------
584
- cell : NotebookNode
585
- The cell which was just processed.
586
- """
587
- loop = get_loop ()
588
- return loop .run_until_complete (
589
- self .async_execute_cell (cell , cell_index , execution_count , store_history )
590
- )
591
-
592
554
async def async_execute_cell (self , cell , cell_index , execution_count = None , store_history = True ):
593
555
"""
594
- Executes a single code cell asynchronously .
556
+ Executes a single code cell.
595
557
596
558
To execute all cells see :meth:`execute`.
597
559
@@ -788,6 +750,24 @@ def _get_buffer_data(self, msg):
788
750
return encoded_buffers
789
751
790
752
753
+ def make_blocking (async_method ):
754
+ def blocking_method (self , * args , ** kwargs ):
755
+ try :
756
+ loop = asyncio .get_event_loop ()
757
+ except RuntimeError :
758
+ loop = asyncio .new_event_loop ()
759
+ asyncio .set_event_loop (loop )
760
+ if self .nest_asyncio :
761
+ import nest_asyncio
762
+ nest_asyncio .apply (loop )
763
+ return loop .run_until_complete (async_method (self , * args , ** kwargs ))
764
+ return blocking_method
765
+
766
+
767
+ NotebookClient .execute = make_blocking (NotebookClient .async_execute )
768
+ NotebookClient .execute_cell = make_blocking (NotebookClient .async_execute_cell )
769
+
770
+
791
771
def execute (nb , cwd = None , km = None , ** kwargs ):
792
772
"""Execute a notebook's code, updating outputs within the notebook object.
793
773
@@ -809,15 +789,3 @@ def execute(nb, cwd=None, km=None, **kwargs):
809
789
if cwd is not None :
810
790
resources ['metadata' ] = {'path' : cwd }
811
791
return NotebookClient (nb = nb , resources = resources , km = km , ** kwargs ).execute ()
812
-
813
-
814
- def get_loop ():
815
- try :
816
- loop = asyncio .get_event_loop ()
817
- except RuntimeError :
818
- loop = asyncio .new_event_loop ()
819
- asyncio .set_event_loop (loop )
820
- return loop
821
-
822
-
823
- nest_asyncio .apply ()
0 commit comments