A kernel type is a reusable implementation of a kind of Jupyter kernel (for
example, ipykernel for Python or ihaskell for Haskell). Users instantiate
a kernel type by writing kernels.<name>.<type> = { … } in their configuration.
This guide explains how to write your own kernel type. For the bigger picture of
how the pieces fit together, see architecture.md.
Start from this template:
{ kernelName, name, config, jupyterConfig, jupyterLib, lib, pkgs, ... }:
jupyterLib.kernelspecKernel {
options = {
/* TODO: options specific to your kernel type */
};
config = {
spec = {
/* TODO: fill in the Jupyter kernel spec */
};
/* optional */ jupyterEnvPackages = pp: [ /* ...packages... */ ];
/* optional */ jupyterExtensions = [ /* ...packages... */ ];
};
}A kernel type is just a NixOS module. jupyterLib.kernelspecKernel wraps your
module so that you only need to fill in a declarative spec and the kernel
directory (outDir) is built for you.
Your module receives the standard NixOS module-system arguments plus a few jupyter.nix-specific ones.
Standard arguments:
config– this module's configuration fixpoint;lib– the Nixpkgs library;pkgs– the Nixpkgs package set (the user'spkgs);name– the last attribute name, which in jupyter.nix is the kernel type, not the kernel name (this is a quirk of how the option type is built; seearchitecture.md);- … all other standard module arguments.
jupyter.nix-specific arguments:
kernelName– the name the user gave this kernel (the second-to-last attribute name, i.e. the<name>inkernels.<name>.<type>); use this fordisplay_name, for instance;jupyterConfig– the top-level jupyter.nix configuration. Useful fields includejupyterConfig.pkgsandjupyterConfig.pythonInterpreter(so your kernel can match the Python interpreter Jupyter itself uses);jupyterLib– the jupyter.nix library, which provideskernelspecKernelandbuildKernelSpec.
Your goal is to populate the spec option, which mirrors a
Jupyter kernel spec. The required fields are argv,
display_name, and language; logos and a few other fields are optional. See
jupyter/kernelspec/module.nix for the full
list of available options.
Besides the kernel itself, you can influence the Jupyter server environment:
jupyterEnvPackages– a selector (pp: [ … ]) for Python packages that must be installed into the environment Jupyter runs from. Use this for packages that need to live in both the kernel and the server (for example, Matplotlib'sipympl).jupyterExtensions– a list of packages providing Jupyter Lab extensions required by your kernel. Be careful to keep extension package sets compatible with the Jupyter Lab version in use.
You do not need to declare outDir, jupyterEnvPackages, or
jupyterExtensions as options yourself — they come from the common kernel
interface (jupyter/kernel/module.nix).
kernelspecKernel mixes in the specKernel helper
(jupyter/kernelspec/lib.nix), which adds:
-
extraPath– a list of directories to prepend to the kernel'sPATHat runtime. This is handy when your kernel needs extra executables available:config.extraPath = [ "${lib.getBin pkgs.hello}/bin" ];
kernelspecKernel is only a convenience. If you already have a kernelspec
directory, or want to build it yourself, skip kernelspecKernel and assign the
directory path to outDir directly:
{ ... }:
{
imports = [ /* the kernel interface is added for you by jupyter.nix */ ];
config.outDir = /* a path to a directory with kernel.json, logos, … */;
}See the custom-dir-kernel example in examples.md.
To make a kernel type usable, it must be in the kernelTypes registry.
Users can register a kernel type without modifying jupyter.nix:
jupyter.lib.makeJupyterLab {
# ...
kernelTypes = {
yourNewKernelType = ./path/to/your/kernel-type.nix;
};
kernels = {
"my-kernel".yourNewKernelType = { /* ... */ };
};
}If you are contributing a kernel type to jupyter.nix,
drop the implementation file in jupyter/kernel-types/
and add it to jupyter/kernel-types.nix.
You can also distribute a kernel type independently; users activate it via
kernelTypes as shown above.