Copyright | (c) Andrew Gibiansky, 2016 |
---|---|
License | MIT |
Maintainer | andrew.gibiansky@gmail.com |
Stability | stable |
Portability | POSIX |
Safe Haskell | None |
Language | Haskell2010 |
The Kernel
module provides an API for quickly and easily creating Jupyter kernels.
Jupyter kernels are programs which communicate using the Jupyter messaging spec; most kernels are language backends that allow using a particular programming language with Jupyter frontends such as the notebook or QtConsole.
To run a kernel, call the serve
function, which provides a type-safe implementation of the Jupyter
messaging spec.
More information about the client and kernel interfaces can be found on the jupyter
README, and several example kernels may be found in
the examples directory.
- serve :: KernelProfile -> CommHandler -> ClientRequestHandler -> IO ()
- serveDynamic :: (KernelProfile -> IO ()) -> CommHandler -> ClientRequestHandler -> IO ()
- data KernelCallbacks = KernelCallbacks {
- sendKernelOutput :: KernelOutput -> IO ()
- sendComm :: Comm -> IO ()
- sendKernelRequest :: KernelRequest -> IO ClientReply
- simpleKernelInfo :: Text -> KernelInfo
- type ClientRequestHandler = KernelCallbacks -> ClientRequest -> IO KernelReply
- defaultClientRequestHandler :: KernelProfile -> KernelInfo -> ClientRequestHandler
- type CommHandler = KernelCallbacks -> Comm -> IO ()
- defaultCommHandler :: CommHandler
- data KernelProfile = KernelProfile {}
- readProfile :: FilePath -> IO (Maybe KernelProfile)
Serving kernels
:: KernelProfile | The kernel profile specifies how to listen for client messages (ports, transport mechanism, message signing, etc). |
-> CommHandler | The |
-> ClientRequestHandler | The request handler is called when |
-> IO () |
Indefinitely serve a kernel on the provided ports. If the ports are not open, fails with an exception.
This starts several threads which listen and write to ZeroMQ sockets on the ports indicated in
the KernelProfile
. If an exception is raised and any of the threads die, the exception is
re-raised on the main thread.
Using this function generally requires a bit of setup. The most common pattern for use is as follows:
- In your kernel
Main.hs
, parse command line arguments. Some combination of arguments should include a path to a connection file; this file can be read and parsed into aKernelProfile
withreadProfile
. (Often, the same executable will have a different mode that installs the kernel usinginstallKernelspec
). - Set up any state your kernel may need, storing it in an
MVar
orIORef
. - Define your
CommHandler
andClientRequestHandler
handlers, which read from the state and reply with any necessary messages. (These handlers may be called concurrently from different threads!) - Provide the kernel profile and handlers to the
serve
function, which blocks indefinitely.
Example kernels may be found in the examples directory.
:: (KernelProfile -> IO ()) | This function is called with the dynamically-generated kernel profile that the kernel will serve on, so that clients may be notified of which ports to use to connect to this kernel. The callback is called after sockets are bound but before the kernel begins listening for messages, so if the callback fails with an exception the kernel threads are never started. |
-> CommHandler | The |
-> ClientRequestHandler | The request handler is called when |
-> IO () |
Indefinitely serve a kernel on some ports. Ports are allocated dynamically and so, unlike
serve
, serveDynamic
may be used when you do not know which ports are open or closed.
The ports allocated by serveDynamic
are passed to the provided callback in the KernelProfile
so that clients may connect to the served kernel.
After the callback is run, several threads are started which listen and write to ZeroMQ sockets on the allocated ports. If an exception is raised and any of the threads die, the exception is re-raised on the main thread. Otherwise, this listens on the kernels indefinitely after running the callback.
This function serves as a form of inverting control over the allocated ports: usually, clients will choose what ports to listen on, and provide the kernel with the ports with a connection file path in the kernel command-line arguments. With this function, you can instead first start the kernel, and then connect a client to the ports that the kernel chooses to bind to.
data KernelCallbacks Source
The KernelCallbacks
data type contains callbacks that the kernel may use to communicate with
the client. Specifically, it can send KernelOutput
and Comm
messages using sendKernelOutput
and sendComm
, respectively, which are often sent to frontends in response to ExecuteRequest
messsages.
In addition, sentKernelRequest
can be used to send a KernelRequest
to the client, and
synchronously wait and receive a ClientReply
.
KernelCallbacks | |
|
Defining a kernel
:: Text | Kernel name, used for |
-> KernelInfo |
Create the simplest possible KernelInfo
.
Defaults version numbers to "0.0", mimetype to "text/plain", empty banner, and a ".txt" file extension.
Mostly intended for use in tutorials and demonstrations; if publishing production kernels, make
sure to use the full KernelInfo
constructor.
>>>
let kernelInfo = simpleKernelInfo "python3"
type ClientRequestHandler = KernelCallbacks -> ClientRequest -> IO KernelReply Source
When calling serve
, the caller must provide a ClientRequestHandler
.
The handler is used when the kernel receives a ClientRequest
message from a frontend; the
ClientRequest
message is passed to the handler, along with a set of callbacks the handler may
use to send messages to the client.
The handler must return a KernelReply
to be sent in response to this request. ClientRequest
and KernelReply
constructors come in pairs, and the output reply constructor must match the
input request constructor.
Note: When the request is a ExecuteRequest
with the executeSilent
option set to True
, the
KernelReply
will not be sent.
defaultClientRequestHandler Source
:: KernelProfile | The profile this kernel is running on. Used to
respond to |
-> KernelInfo | Information about this kernel. Used to respond to
|
-> ClientRequestHandler |
Handler which responds to all ClientRequest
messages with a default, empty reply.
type CommHandler = KernelCallbacks -> Comm -> IO () Source
When calling serve
, the caller must provide a CommHandler
.
The handler is used when the kernel receives a Comm
message from a frontend; the Comm
message
is passed to the handler, along with a set of callbacks the handler may use to send messages to
the client.
Since Comm
s are used for free-form communication outside the messaging spec, kernels should
ignore Comm
messages they do not expect.
The defaultCommHandler
handler is provided for use with kernels that wish to ignore all Comm
messages.
defaultCommHandler :: CommHandler Source
Handler which ignores all Comm
messages sent to the kernel (and does nothing).
Reading Kernel Profiles
data KernelProfile Source
A kernel profile, specifying how the kernel communicates.
The kernel profile is usually obtained by a kernel by parsing the connection file passed to it as an argument as indicated by the kernelspec.
The profileTransport
, profileIp
and five profile Port fields specify the ports which the
kernel should bind to. These ports are usually generated fresh for every client or server started.
profileSignatureKey
is used to cryptographically sign messages, so that other users on the
system can’t send code to run in this kernel. See the
wire protocol documentation
for the details of how this signature is calculated.
More info on the fields of the connection file and the KernelProfile
is available in the
respective Jupyter documentation.
KernelProfile | |
|
Eq KernelProfile Source | |
Ord KernelProfile Source | |
Read KernelProfile Source | |
Show KernelProfile Source | |
ToJSON KernelProfile Source | Instance to decode a |
FromJSON KernelProfile Source | Decode a This object is passed to kernels in the connection file. |
readProfile :: FilePath -> IO (Maybe KernelProfile) Source
Read a KernelProfile
from a file. This file (the connection file) should contain a
JSON-encoded object with all necessary fields, as described in the
connection files
section of the Jupyter documentation.
If the file contents cannot be parsed, Nothing
is returned.