Add a pre- and post-execution hooks
Opened this issue · 5 comments
Problem
- When the notebook is closed the metadata for cell timing are not updated
- When the notebook is closed and execution of a long-running cell completes, there is no way for extensions to offer notifications (e.g. sending an email)
Proposed Solution
Allow extensions to register pre- and post-execution functions to run which would be allowed to populate the cell metadata.
Maybe special-case execution timing as a hook that is available by default, and enabled based on an argument passed to the JSON API?
Additional context
The timing metadata population is implemented in JupyterLab here:
I see there is a TODO note in this repository about it:
jupyter-server-nbmodel/src/executor.ts
Line 122 in da00ed8
To give some more context here:
- the pre-post execution hooks should be implemented in
jupyter-serverextension to allow to perform a side-effect even if the browser is not connected (for frontend there are signals already) - there is a couple of options for how to enable the registration of hooks
- via an entry points mechanism similar to how
jupyter-aiuses for model providers - via a Python API on the jupyter-server extension, similarly to how
jupyter-fileidexposes a small public API or howjupyter-server-ydocdoes - via configuration as
jupyter-serverdoes forpre_save_hookandpost_save_hook
- via an entry points mechanism similar to how
- timing could be implemented either using such hooks, or directly in
_execute_snippetby measuring time before and afterclient.execute_interactivecall:
jupyter-server-nbmodel/jupyter_server_nbmodel/handlers.py
Lines 203 to 249 in ef8181c
- special-casing execution timing would be desirable for two reasons:
- if another pre/post execution hook is added which takes non-negligible time to execute, it could be omitted from timing if we special case it (otherwise the timing results could depend on order of hook registration which I think would be confusing)
- we can add an argument to the REST API to enable it, improving the feature parity with the default code execution mechanism if the
runCellin typescript automatically passes this argument if user enabled therecordTimingoption.
@fcollonval @echarles any preferences on how we should implement hooks? On frontend these are just ISignals. Let me number the options I listed above:
- a) via an entry points mechanism similar to how
jupyter-aiuses for model providers - b) via a Python API on the jupyter-server extension, similarly to how
jupyter-fileidexposes a small public API or howjupyter-server-ydocdoes - c) via configuration as
jupyter-serverdoes forpre_save_hookandpost_save_hook
My own thoughs:
- (a) is convenient to use once you know how entry points work:
- requires writing a python package
- the hook implementation can be a pure Python function
- possibly slightly harder to test on CI
- (b) seems easier from maintenance point as this is just an extra method on extension app but documented as public:
- requires writing a server extension to get access to this extension's API
- thus this also requires creating a python package
- (c) would be in harmony with how jupyter-server does hooks:
- can be used without requiring a python package,
- could use a Set or List traitlet to allow registering multiple hooks
Thanks for raising the question @krassowski
Small question related to the need and setting aside the specific case of timing, would using the Jupyter Server events answer the need or do you see other cases like the timing that the reaction should be called immediately?
Great point! For the use case of "send me an email when execution finished", I think Jupyter Server event could be good enough.
For feature parity: frontend has onCellExecuted, selectionExecuted, onCellExecutionScheduled signals which can perform side effects on the cell or notebook, but cannot modify the code being executed. Jupyter Server event needs to be serialized to JSON, so these could not include the equivalent cell or notebook object, but I think it would be fine if the event contained:
cellIddocumentIdsuccesskernelError(if any)
It could be interesting to explore pre-execution hooks that would allow to modify the code but I think this is a larger conversation and not required right now.
Shall we just add event emitter then?
Shall we just add event emitter then?
I would go that road as it is anyway a good addition. But let's keep this issue opened for further exploration.