15
15
from jupyter_client .client import KernelClient
16
16
from nbformat import NotebookNode
17
17
from nbformat .v4 import output_from_msg
18
- from traitlets import Any , Bool , Dict , Enum , Integer , List , Type , Unicode , default
18
+ from traitlets import (
19
+ Any ,
20
+ Bool ,
21
+ Callable ,
22
+ Dict ,
23
+ Enum ,
24
+ Integer ,
25
+ List ,
26
+ Type ,
27
+ Unicode ,
28
+ default ,
29
+ )
19
30
from traitlets .config .configurable import LoggingConfigurable
20
31
21
32
from .exceptions import (
26
37
DeadKernelError ,
27
38
)
28
39
from .output_widget import OutputWidget
29
- from .util import ensure_async , run_sync , run_hook
40
+ from .util import ensure_async , run_hook , run_sync
30
41
31
42
32
43
def timestamp (msg : Optional [Dict ] = None ) -> str :
@@ -261,43 +272,85 @@ class NotebookClient(LoggingConfigurable):
261
272
262
273
kernel_manager_class : KernelManager = Type (config = True , help = 'The kernel manager class to use.' )
263
274
264
- on_execution_start : t .Optional [t .Callable ] = Any (
275
+ on_notebook_start : t .Optional [t .Callable ] = Callable (
276
+ default_value = None ,
277
+ allow_none = True ,
278
+ help = dedent (
279
+ """
280
+ Called after the kernel manager and kernel client are setup, and cells
281
+ are about to execute.
282
+ Called with kwargs `notebook`
283
+ """
284
+ ),
285
+ ).tag (config = True )
286
+
287
+ on_notebook_complete : t .Optional [t .Callable ] = Callable (
288
+ default_value = None ,
289
+ allow_none = True ,
290
+ help = dedent (
291
+ """
292
+ Called after kernel is cleaned up.
293
+ Called with kwargs `notebook`
294
+ """
295
+ ),
296
+ ).tag (config = True )
297
+
298
+ on_notebook_error : t .Optional [t .Callable ] = Callable (
299
+ default_value = None ,
300
+ allow_none = True ,
301
+ help = dedent (
302
+ """
303
+ Called when the notebook encountered an error.
304
+ Called with kwargs `notebook`
305
+ """
306
+ ),
307
+ ).tag (config = True )
308
+
309
+ on_cell_start : t .Optional [t .Callable ] = Callable (
265
310
default_value = None ,
266
311
allow_none = True ,
267
- help = dedent ("""
268
- Called after the kernel manager and kernel client are setup, and cells
269
- are about to execute.
270
- Called with kwargs `kernel_id`.
271
- """ ),
312
+ help = dedent (
313
+ """
314
+ A callable which executes before a cell is executed and before non-executing cells
315
+ are skipped.
316
+ Called with kwargs `cell` and `cell_index`.
317
+ """
318
+ ),
272
319
).tag (config = True )
273
320
274
- on_cell_start : t .Optional [t .Callable ] = Any (
321
+ on_cell_execute : t .Optional [t .Callable ] = Callable (
275
322
default_value = None ,
276
323
allow_none = True ,
277
- help = dedent ("""
278
- A callable which executes before a cell is executed.
279
- Called with kwargs `cell`, and `cell_index`.
280
- """ ),
324
+ help = dedent (
325
+ """
326
+ A callable which executes just before a code cell is executed.
327
+ Called with kwargs `cell` and `cell_index`.
328
+ """
329
+ ),
281
330
).tag (config = True )
282
331
283
- on_cell_complete : t .Optional [t .Callable ] = Any (
332
+ on_cell_complete : t .Optional [t .Callable ] = Callable (
284
333
default_value = None ,
285
334
allow_none = True ,
286
- help = dedent ("""
287
- A callable which executes after a cell execution is complete. It is
288
- called even when a cell results in a failure.
289
- Called with kwargs `cell`, and `cell_index`.
290
- """ ),
335
+ help = dedent (
336
+ """
337
+ A callable which executes after a cell execution is complete. It is
338
+ called even when a cell results in a failure.
339
+ Called with kwargs `cell` and `cell_index`.
340
+ """
341
+ ),
291
342
).tag (config = True )
292
343
293
- on_cell_error : t .Optional [t .Callable ] = Any (
344
+ on_cell_error : t .Optional [t .Callable ] = Callable (
294
345
default_value = None ,
295
346
allow_none = True ,
296
- help = dedent ("""
297
- A callable which executes when a cell execution results in an error.
298
- This is executed even if errors are suppressed with `cell_allows_errors`.
299
- Called with kwargs `cell`, and `cell_index`.
300
- """ ),
347
+ help = dedent (
348
+ """
349
+ A callable which executes when a cell execution results in an error.
350
+ This is executed even if errors are suppressed with `cell_allows_errors`.
351
+ Called with kwargs `cell` and `cell_index`.
352
+ """
353
+ ),
301
354
).tag (config = True )
302
355
303
356
@default ('kernel_manager_class' )
@@ -478,10 +531,11 @@ async def async_start_new_kernel_client(self) -> KernelClient:
478
531
try :
479
532
await ensure_async (self .kc .wait_for_ready (timeout = self .startup_timeout ))
480
533
except RuntimeError :
534
+ await run_hook (self .on_notebook_error , notebook = self .nb )
481
535
await self ._async_cleanup_kernel ()
482
536
raise
483
537
self .kc .allow_stdin = False
484
- run_hook (sself . on_execution_start )
538
+ await run_hook (self . on_notebook_start , notebook = self . nb )
485
539
return self .kc
486
540
487
541
start_new_kernel_client = run_sync (async_start_new_kernel_client )
@@ -553,10 +607,13 @@ def on_signal():
553
607
await self .async_start_new_kernel_client ()
554
608
try :
555
609
yield
610
+ except RuntimeError :
611
+ await run_hook (self .on_notebook_error , notebook = self .nb )
612
+ raise
556
613
finally :
557
614
if cleanup_kc :
558
615
await self ._async_cleanup_kernel ()
559
-
616
+ await run_hook ( self . on_notebook_complete , notebook = self . nb )
560
617
atexit .unregister (self ._cleanup_kernel )
561
618
try :
562
619
loop .remove_signal_handler (signal .SIGINT )
@@ -785,11 +842,9 @@ def _passed_deadline(self, deadline: int) -> bool:
785
842
return True
786
843
return False
787
844
788
- def _check_raise_for_error (
789
- self ,
790
- cell : NotebookNode ,
791
- cell_index : int ,
792
- exec_reply : t .Optional [t .Dict ]) -> None :
845
+ async def _check_raise_for_error (
846
+ self , cell : NotebookNode , cell_index : int , exec_reply : t .Optional [t .Dict ]
847
+ ) -> None :
793
848
794
849
if exec_reply is None :
795
850
return None
@@ -803,11 +858,9 @@ def _check_raise_for_error(
803
858
or exec_reply_content .get ('ename' ) in self .allow_error_names
804
859
or "raises-exception" in cell .metadata .get ("tags" , [])
805
860
)
806
-
807
- if (exec_reply is not None ) and exec_reply ['content' ]['status' ] == 'error' :
808
- run_hook (self .on_cell_error , cell = cell , cell_index = cell_index )
809
- if self .force_raise_errors or not cell_allows_errors :
810
- raise CellExecutionError .from_cell_and_msg (cell , exec_reply ['content' ])
861
+ await run_hook (self .on_cell_error , cell = cell , cell_index = cell_index )
862
+ if not cell_allows_errors :
863
+ raise CellExecutionError .from_cell_and_msg (cell , exec_reply_content )
811
864
812
865
async def async_execute_cell (
813
866
self ,
@@ -850,6 +903,9 @@ async def async_execute_cell(
850
903
The cell which was just processed.
851
904
"""
852
905
assert self .kc is not None
906
+
907
+ await run_hook (self .on_cell_start , cell = cell , cell_index = cell_index )
908
+
853
909
if cell .cell_type != 'code' or not cell .source .strip ():
854
910
self .log .debug ("Skipping non-executing cell %s" , cell_index )
855
911
return cell
@@ -867,13 +923,13 @@ async def async_execute_cell(
867
923
self .allow_errors or "raises-exception" in cell .metadata .get ("tags" , [])
868
924
)
869
925
870
- run_hook (self .on_cell_start , cell = cell , cell_index = cell_index )
926
+ await run_hook (self .on_cell_execute , cell = cell , cell_index = cell_index )
871
927
parent_msg_id = await ensure_async (
872
928
self .kc .execute (
873
929
cell .source , store_history = store_history , stop_on_error = not cell_allows_errors
874
930
)
875
931
)
876
- run_hook (self .on_cell_complete , cell = cell , cell_index = cell_index )
932
+ await run_hook (self .on_cell_complete , cell = cell , cell_index = cell_index )
877
933
# We launched a code cell to execute
878
934
self .code_cells_executed += 1
879
935
exec_timeout = self ._get_timeout (cell )
@@ -907,7 +963,7 @@ async def async_execute_cell(
907
963
908
964
if execution_count :
909
965
cell ['execution_count' ] = execution_count
910
- self ._check_raise_for_error (cell , cell_index , exec_reply )
966
+ await self ._check_raise_for_error (cell , cell_index , exec_reply )
911
967
self .nb ['cells' ][cell_index ] = cell
912
968
return cell
913
969
0 commit comments