Package: kernel-sidecar
Kernel SidecarOverviewkernel-sidecarprovides the building blocks for creating applications that run alongside Jupyter Kernels, keeping the Notebook document model in memory and acting as a middle-man between the Kernel and all other clients. There are many potential features that can be built into a Sidecar application:Persisting Outputsfor cell execution even when no clients are connected (e.g. run a cell, close your browser, come back later and see the output)Code Cell Lintingsuch asblackorisortVariable Explorerthat can be sent to newly connected clients without needing to execute code in the KernelCustom Execution Managersto handle DAG/dependent cell execution or to dequeue queued cells without sending an interrupt to the Kernel which dequeues all queued cellsInstallationpipinstallkernel-sidecarKey ConceptsKernelSidecarClientTheKernelSidecarClientclass manages the ZMQ connections to the Kernel, sending execute request or other messages to the Kernel, and processing messages coming back from the Kernel oniopub,shell,control, andstdinchannels. All messages sent and received are modeled with Pydantic. When preparing to send a request to the Kernel, it's structured as aKernelActionwhich connects the request with zero-to-many callbacks for responses to that specific request.KernelActionKernelActionconnects three key pieces of the request-reply flow between the sidecar application and the Kernel:Store the sent Request messageDelegate messages to callbacks based on the request messageMake the flow "awaitable" based on the Request type, e.g. anexecute_requestis done whenexecute_replyorerroris received andstatushas gone toidle.HandlersWhen theKernelSidecarClientreceives a message over ZMQ, parses it into a Pydantic model, and delegates it to the appropriateActionto be handled, it passes on that message to everyHandlerattached to theActionand awaits all of them to handle that message.Handlerobjects can define handling different message types by creating methods named for the message type, e.g.async def handle_display_data. Seehandlers.DebugHandlerorcli.OutputHandlerfor examples of custom Handlers.CommsComms are a flexible way for a client and the Kernel to send messages outside of theexecute_requestformat. The most widely used package that utilizes Comms is probablyipywidgets, but Comms in general are a very powerful tool for a Sidecar application. A Comm can be opened by either the Sidecar or the Kernel. A target for that Comm should be registered on the other side before the open happens. It's probably most typical to register a Comm target in the Kernel by sending anexecute_request, then sending acomm_openfrom the Sidecar side. Seetests/test_comms.pyfor examples.Once a Comm is open, it has a uniquecomm_id.KernelSidecarClientwill automatically route allcomm_msgmessages to aCommHandlerinstance bycomm_idin thecomm_msgcontent. That routing pattern is a bit confusing as it overlaps theHandler/Actionpattern, but it's necessary becausecomm_msgcan come in as a result ofexecute_request's orcomm_msg's or potentially other messages. So theCommManager->CommHandlerrouting basically needs to be applied to every message theKernelSidecarClientreceives over ZMQ.Modelskernel-sidecarhas Pydantic models for:The Jupyter Notebook document (models/notebook.py), which should be consistent withnbformatparsing / structureRequest messages sent to the Kernel over ZMQ (models/requests.py)Messages received over ZMQ from the Kernel (models/messages.py)CLIkernel-sidecarships a small CLI for testing a connection to a Kernel.❯sidecar--help
Usage:sidecar[OPTIONS]Options:-fFILEKernelconnectionfile[required]--debug/--no-debugTurnonDEBUGlogging[default:no-debug]--executeTEXTExecutecodestringinsteadofsendingkernelinforequest--tail/--no-tailContinuetailingZMQafterconnectingorexecutingcode[default:no-tail]--install-completion[bash|zsh|fish|powershell|pwsh]Installcompletionforthespecifiedshell.--show-completion[bash|zsh|fish|powershell|pwsh]Showcompletionforthespecifiedshell,tocopyitorcustomizetheinstallation.--helpShowthismessageandexit.Try it out by starting an IPython kernel in one terminal and using the CLI in another.python-mipykernel_launcher--debug-f/tmp/kernel.jsonkernel-sidecaronrelease-0.3.2[$?]is📦v0.3.1via🐍v3.11.0(kernel-sidecar-py3.11)❯sidecar-f/tmp/kernel.json2023-03-10T14:31:59.992235Z[info]Attemptingtoconnect:{'control_port':34897,'hb_port':49821,'iopub_port':40577,'ip':'127.0.0.1','kernel_name':'','key':'615bcebc-baf2e28abad1f6c017dc71dc','shell_port':37421,'signature_scheme':'hmac-sha256','stdin_port':41405,'transport':'tcp'}[kernel_sidecar.cli]filename=cli.pyfunc_name=mainlineno=622023-03-10T14:32:00.026503Z[info]{'banner':'Python 3.11.0 (main, Nov  7 2022, 09:38:45) [GCC 9.4.0]\n'"Type 'copyright', 'credits' or 'license' for more information\n""IPython 8.10.0 -- An enhanced Interactive Python. Type '?' for "'help.\n','debugger':None,'help_links':[{'text':'Python Reference','url':'https://docs.python.org/3.11'},{'text':'IPython Reference','url':'https://ipython.org/documentation.html'},{'text':'NumPy Reference','url':'https://docs.scipy.org/doc/numpy/reference/'},{'text':'SciPy Reference','url':'https://docs.scipy.org/doc/scipy/reference/'},{'text':'Matplotlib Reference','url':'https://matplotlib.org/contents.html'},{'text':'SymPy Reference','url':'http://docs.sympy.org/latest/index.html'},{'text':'pandas Reference','url':'https://pandas.pydata.org/pandas-docs/stable/'}],'implementation':'ipython','implementation_version':'8.10.0','language_info':{'codemirror_mode':{'name':'ipython','version':3},'file_extension':'.py','mimetype':'text/x-python','name':'python','nbconvert_exporter':'python','pygments_lexer':'ipython3','version':'3.11.0'},'protocol_version':'5.3','status':'ok'}[kernel_sidecar.cli]filename=cli.pyfunc_name=connectlineno=44❯sidecar-f/tmp/kernel.json--execute"print('Hello, World'); 1/0"2023-03-10T14:33:27.394935Z[info]Attemptingtoconnect:{'control_port':34897,'hb_port':49821,'iopub_port':40577,'ip':'127.0.0.1','kernel_name':'','key':'615bcebc-baf2e28abad1f6c017dc71dc','shell_port':37421,'signature_scheme':'hmac-sha256','stdin_port':41405,'transport':'tcp'}[kernel_sidecar.cli]filename=cli.pyfunc_name=mainlineno=622023-03-10T14:33:27.629630Z[info]Hello,World[kernel_sidecar.cli]filename=cli.pyfunc_name=handle_streamlineno=232023-03-10T14:33:27.702700Z[error]divisionbyzero[kernel_sidecar.cli]filename=cli.pyfunc_name=handle_errorlineno=31