28
28
CellExecutionComplete ,
29
29
CellExecutionError
30
30
)
31
- from .util import run_sync , ensure_async
31
+ from .util import run_sync , ensure_async , run_hook
32
32
from .output_widget import OutputWidget
33
33
34
34
@@ -227,6 +227,45 @@ class NotebookClient(LoggingConfigurable):
227
227
228
228
kernel_manager_class : KernelManager = Type (config = True , help = 'The kernel manager class to use.' )
229
229
230
+ on_execution_start : t .Optional [t .Callable ] = Any (
231
+ default_value = None ,
232
+ allow_none = True ,
233
+ help = dedent ("""
234
+ Called after the kernel manager and kernel client are setup, and cells
235
+ are about to execute.
236
+ Called with kwargs `kernel_id`.
237
+ """ ),
238
+ ).tag (config = True )
239
+
240
+ on_cell_start : t .Optional [t .Callable ] = Any (
241
+ default_value = None ,
242
+ allow_none = True ,
243
+ help = dedent ("""
244
+ A callable which executes before a cell is executed.
245
+ Called with kwargs `cell`, and `cell_index`.
246
+ """ ),
247
+ ).tag (config = True )
248
+
249
+ on_cell_complete : t .Optional [t .Callable ] = Any (
250
+ default_value = None ,
251
+ allow_none = True ,
252
+ help = dedent ("""
253
+ A callable which executes after a cell execution is complete. It is
254
+ called even when a cell results in a failure.
255
+ Called with kwargs `cell`, and `cell_index`.
256
+ """ ),
257
+ ).tag (config = True )
258
+
259
+ on_cell_error : t .Optional [t .Callable ] = Any (
260
+ default_value = None ,
261
+ allow_none = True ,
262
+ help = dedent ("""
263
+ A callable which executes when a cell execution results in an error.
264
+ This is executed even if errors are suppressed with `cell_allows_errors`.
265
+ Called with kwargs `cell`, and `cell_index`.
266
+ """ ),
267
+ ).tag (config = True )
268
+
230
269
@default ('kernel_manager_class' )
231
270
def _kernel_manager_class_default (self ) -> KernelManager :
232
271
"""Use a dynamic default to avoid importing jupyter_client at startup"""
@@ -412,6 +451,7 @@ async def async_start_new_kernel_client(self, **kwargs) -> t.Tuple[KernelClient,
412
451
await self ._async_cleanup_kernel ()
413
452
raise
414
453
self .kc .allow_stdin = False
454
+ run_hook (self .on_execution_start , kernel_id = kernel_id )
415
455
return self .kc , kernel_id
416
456
417
457
start_new_kernel_client = run_sync (async_start_new_kernel_client )
@@ -702,14 +742,16 @@ def _passed_deadline(self, deadline: int) -> bool:
702
742
def _check_raise_for_error (
703
743
self ,
704
744
cell : NotebookNode ,
745
+ cell_index : int ,
705
746
exec_reply : t .Optional [t .Dict ]) -> None :
706
747
707
748
cell_allows_errors = self .allow_errors or "raises-exception" in cell .metadata .get (
708
749
"tags" , []
709
750
)
710
751
711
- if self .force_raise_errors or not cell_allows_errors :
712
- if (exec_reply is not None ) and exec_reply ['content' ]['status' ] == 'error' :
752
+ if (exec_reply is not None ) and exec_reply ['content' ]['status' ] == 'error' :
753
+ run_hook (self .on_cell_error , cell = cell , cell_index = cell_index )
754
+ if self .force_raise_errors or not cell_allows_errors :
713
755
raise CellExecutionError .from_cell_and_msg (cell , exec_reply ['content' ])
714
756
715
757
async def async_execute_cell (
@@ -760,13 +802,15 @@ async def async_execute_cell(
760
802
cell ['metadata' ]['execution' ] = {}
761
803
762
804
self .log .debug ("Executing cell:\n %s" , cell .source )
805
+ run_hook (self .on_cell_start , cell = cell , cell_index = cell_index )
763
806
parent_msg_id = await ensure_async (
764
807
self .kc .execute (
765
808
cell .source ,
766
809
store_history = store_history ,
767
810
stop_on_error = not self .allow_errors
768
811
)
769
812
)
813
+ run_hook (self .on_cell_complete , cell = cell , cell_index = cell_index )
770
814
# We launched a code cell to execute
771
815
self .code_cells_executed += 1
772
816
exec_timeout = self ._get_timeout (cell )
@@ -792,7 +836,7 @@ async def async_execute_cell(
792
836
793
837
if execution_count :
794
838
cell ['execution_count' ] = execution_count
795
- self ._check_raise_for_error (cell , exec_reply )
839
+ self ._check_raise_for_error (cell , cell_index , exec_reply )
796
840
self .nb ['cells' ][cell_index ] = cell
797
841
return cell
798
842
0 commit comments