-
-
Notifications
You must be signed in to change notification settings - Fork 18.2k
nixos/jupyter: init service #33673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nixos/jupyter: init service #33673
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| { config, lib, pkgs, ... }: | ||
|
|
||
| with lib; | ||
|
|
||
| let | ||
|
|
||
| cfg = config.services.jupyter; | ||
|
|
||
|
|
||
| kernels = (pkgs.jupyterKernels.create { | ||
| kernelDefinitions = if cfg.kernels != null | ||
| then cfg.kernels | ||
| else pkgs.jupyterKernels.defaultKernelDefinition; | ||
| }); | ||
|
|
||
| notebookConfig = pkgs.writeText "jupyter_config.py" '' | ||
| ${cfg.notebookConfig} | ||
|
|
||
| c.NotebookApp.password = ${cfg.password} | ||
| ''; | ||
|
|
||
| in { | ||
| meta.maintainers = with maintainers; [ aborsu ]; | ||
|
|
||
| options.services.jupyter = { | ||
| enable = mkEnableOption "Jupyter development server"; | ||
|
|
||
| ip = mkOption { | ||
| type = types.str; | ||
| default = "localhost"; | ||
| description = '' | ||
| IP address Jupyter will be listening on. | ||
| ''; | ||
| }; | ||
|
|
||
| port = mkOption { | ||
| type = types.int; | ||
| default = 8888; | ||
| description = '' | ||
| Port number Jupyter will be listening on. | ||
| ''; | ||
| }; | ||
|
|
||
| notebookDir = mkOption { | ||
| type = types.str; | ||
| default = "~/"; | ||
| description = '' | ||
| Root directory for notebooks. | ||
| ''; | ||
| }; | ||
|
|
||
| user = mkOption { | ||
| type = types.str; | ||
| default = "jupyter"; | ||
| description = '' | ||
| Name of the user used to run the jupyter service. | ||
| For security reason, jupyter should really not be run as root. | ||
| If not set (jupyter), the service will create a jupyter user with appropriate settings. | ||
| ''; | ||
| example = "aborsu"; | ||
| }; | ||
|
|
||
| group = mkOption { | ||
| type = types.str; | ||
| default = "jupyter"; | ||
| description = '' | ||
| Name of the group used to run the jupyter service. | ||
| Use this if you want to create a group of users that are able to view the notebook directory's content. | ||
| ''; | ||
| example = "users"; | ||
| }; | ||
|
|
||
| password = mkOption { | ||
| type = types.str; | ||
| description = '' | ||
| Password to use with notebook. | ||
| Can be generated using: | ||
| In [1]: from notebook.auth import passwd | ||
| In [2]: passwd('test') | ||
| Out[2]: 'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba' | ||
| NOTE: you need to keep the single quote inside the nix string. | ||
| Or you can use a python oneliner: | ||
| "open('/path/secret_file', 'r', encoding='utf8').read().strip()" | ||
| It will be interpreted at the end of the notebookConfig. | ||
| ''; | ||
| example = [ | ||
| "'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'" | ||
| "open('/path/secret_file', 'r', encoding='utf8').read().strip()" | ||
| ]; | ||
| }; | ||
|
|
||
| notebookConfig = mkOption { | ||
| type = types.lines; | ||
| default = ""; | ||
| description = '' | ||
| Raw jupyter config. | ||
| ''; | ||
| }; | ||
|
|
||
| kernels = mkOption { | ||
| type = types.nullOr (types.attrsOf(types.submodule (import ./kernel-options.nix { | ||
| inherit lib; | ||
| }))); | ||
|
|
||
| default = null; | ||
| example = literalExample '' | ||
| { | ||
| python3 = let | ||
| env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ | ||
| ipykernel | ||
| pandas | ||
| scikitlearn | ||
| ])); | ||
| in { | ||
| displayName = "Python 3 for machine learning"; | ||
| argv = [ | ||
| "$ {env.interpreter}" | ||
| "-m" | ||
| "ipykernel_launcher" | ||
| "-f" | ||
| "{connection_file}" | ||
| ]; | ||
| language = "python"; | ||
| logo32 = "$ {env.sitePackages}/ipykernel/resources/logo-32x32.png"; | ||
| logo64 = "$ {env.sitePackages}/ipykernel/resources/logo-64x64.png"; | ||
| }; | ||
| } | ||
| ''; | ||
| description = "Declarative kernel config | ||
|
|
||
| Kernels can be declared in any language that supports and has the required | ||
| dependencies to communicate with a jupyter server. | ||
| In python's case, it means that ipykernel package must always be included in | ||
| the list of packages of the targeted environment. | ||
| "; | ||
| }; | ||
| }; | ||
|
|
||
| config = mkMerge [ | ||
| (mkIf cfg.enable { | ||
| systemd.services.jupyter = { | ||
| description = "Jupyter development server"; | ||
|
|
||
| wantedBy = [ "multi-user.target" ]; | ||
|
|
||
| path = [ pkgs.bash ]; # needed for sh in cell magic to work | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you considered patching |
||
|
|
||
| environment = { | ||
| JUPYTER_PATH = toString kernels; | ||
| }; | ||
|
|
||
| serviceConfig = { | ||
| Restart = "always"; | ||
| ExecStart = ''${pkgs.python3.pkgs.notebook}/bin/jupyter-notebook \ | ||
| --no-browser \ | ||
| --ip=${cfg.ip} \ | ||
| --port=${toString cfg.port} --port-retries 0 \ | ||
| --notebook-dir=${cfg.notebookDir} \ | ||
| --NotebookApp.config_file=${notebookConfig} | ||
| ''; | ||
| User = cfg.user; | ||
| Group = cfg.group; | ||
| WorkingDirectory = "~"; | ||
| }; | ||
| }; | ||
| }) | ||
| (mkIf (cfg.enable && (cfg.group == "jupyter")) { | ||
| users.groups.jupyter = {}; | ||
| }) | ||
| (mkIf (cfg.enable && (cfg.user == "jupyter")) { | ||
| users.extraUsers.jupyter = { | ||
| extraGroups = [ cfg.group ]; | ||
| home = "/var/lib/jupyter"; | ||
| createHome = true; | ||
| useDefaultShell = true; # needed so that the user can start a terminal. | ||
| }; | ||
| }) | ||
| ]; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # Options that can be used for creating a jupyter kernel. | ||
| {lib }: | ||
|
|
||
| with lib; | ||
|
|
||
| { | ||
| options = { | ||
|
|
||
| displayName = mkOption { | ||
| type = types.str; | ||
| default = ""; | ||
| example = [ | ||
| "Python 3" | ||
| "Python 3 for Data Science" | ||
| ]; | ||
| description = '' | ||
| Name that will be shown to the user. | ||
| ''; | ||
| }; | ||
|
|
||
| argv = mkOption { | ||
| type = types.listOf types.str; | ||
| example = [ | ||
| "{customEnv}/bin/python" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ${customEnv.interpreter} |
||
| "-m" | ||
| "ipykernel_launcher" | ||
| "-f" | ||
| "{connection_file}" | ||
| ]; | ||
| description = '' | ||
| Command and arguments to start the kernel. | ||
| ''; | ||
| }; | ||
|
|
||
| language = mkOption { | ||
| type = types.str; | ||
| example = "python"; | ||
| description = '' | ||
| Language of the environment. Typically the name of the binary. | ||
| ''; | ||
| }; | ||
|
|
||
| logo32 = mkOption { | ||
| type = types.nullOr types.path; | ||
| default = null; | ||
| example = "{env}/lib/python3.6/site-packages/ipykernel/resources/logo-32x32.png"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ${env.sitePackages} |
||
| description = '' | ||
| Path to 32x32 logo png. | ||
| ''; | ||
| }; | ||
| logo64 = mkOption { | ||
| type = types.nullOr types.path; | ||
| default = null; | ||
| example = "{env}/lib/python3.6/site-packages/ipykernel/resources/logo-64x64.png"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ${env.sitePackages} |
||
| description = '' | ||
| Path to 64x64 logo png. | ||
| ''; | ||
| }; | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| { python3 | ||
| , jupyterKernels | ||
| , kernelDefinitions ? jupyterKernels.defaultKernelDefinition | ||
|
||
| }: | ||
|
|
||
| let | ||
|
|
||
| jupyterPath = (jupyterKernels.create { kernelDefinitions = kernelDefinitions; }); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inherit / space |
||
|
|
||
| in | ||
|
|
||
| with python3.pkgs; toPythonModule ( | ||
| notebook.overrideAttrs(oldAttrs: { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. overridePythonAttrs |
||
| makeWrapperArgs = ["--set JUPYTER_PATH ${jupyterPath}"]; | ||
| }) | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| { lib, pkgs, stdenv}: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no |
||
|
|
||
| let | ||
|
|
||
| defaultKernelDefinition = { | ||
| python3 = let | ||
| env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ | ||
| ipykernel | ||
| pandas | ||
| scikitlearn | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these Jupyter dependencies or just nice to have packages?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ipykernel is needed, the others are just basic packages used for data science.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep the default as minimal as possible. |
||
| ])); | ||
| in { | ||
| displayName = "Python 3"; | ||
| argv = [ | ||
| "${env.interpreter}" | ||
| "-m" | ||
| "ipykernel_launcher" | ||
| "-f" | ||
| "{connection_file}" | ||
| ]; | ||
| language = "python"; | ||
| logo32 = "${env.sitePackages}/ipykernel/resources/logo-32x32.png"; | ||
| logo64 = "${env.sitePackages}/ipykernel/resources/logo-64x64.png"; | ||
| }; | ||
| }; | ||
|
|
||
| in | ||
| { | ||
| defaultKernelDefinition = defaultKernelDefinition; | ||
| create = { kernelDefinitions ? defaultKernelDefinition }: | ||
|
||
| with lib; stdenv.mkDerivation rec { | ||
| name = "jupyter-kernels"; | ||
|
|
||
| src = "/dev/null"; | ||
|
|
||
| unpackCmd ="mkdir jupyter_kernels"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. space |
||
|
|
||
| installPhase = '' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. space |
||
| mkdir kernels | ||
|
|
||
| ${concatStringsSep "\n" (mapAttrsToList (kernelName: kernel: | ||
| let | ||
| config = builtins.toJSON { | ||
| display_name = if (kernel.displayName != "") | ||
| then kernel.displayName | ||
| else kernelName; | ||
| argv = kernel.argv; | ||
| language = kernel.language; | ||
| }; | ||
| logo32 = | ||
| if (kernel.logo32 != null) | ||
| then "ln -s ${kernel.logo32} 'kernels/${kernelName}/logo-32x32.png';" | ||
| else ""; | ||
| logo64 = | ||
| if (kernel.logo64 != null) | ||
| then "ln -s ${kernel.logo64} 'kernels/${kernelName}/logo-64x64.png';" | ||
| else ""; | ||
| in '' | ||
| mkdir 'kernels/${kernelName}'; | ||
| echo '${config}' > 'kernels/${kernelName}/kernel.json'; | ||
| ${logo32} | ||
| ${logo64} | ||
| '') kernelDefinitions)} | ||
|
|
||
| mkdir $out | ||
| cp -r kernels $out | ||
| ''; | ||
|
|
||
| meta = { | ||
| description = "Wrapper to create jupyter notebook kernel definitions"; | ||
| homepage = http://jupyter.org/; | ||
| # NIXOS license as this is a nixos meta package. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need |
||
| maintainers = with maintainers; [ aborsu ]; | ||
| }; | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've basically had to move the null check here now.
I though of passing the
pkgs.jupyterKernels.defaultKernelConfigas the default value forjupyter.kernels, but the it complains that it contains a reference to a derivation. (I can understand that having to build a derivation to create the doc is not the greatest).