Using custom kernels to offer different sets of packages to user groups

Hi all,

I am trying to set JupyterHub so that each user group can choose from a different set of available preconfigured packages. I figured out this might be done via venv’s (one for each group), but there are some issues. I installed the new venv, jupyterhub sees it and allows to create a new kernel. So far so good. However, any packages that are installed in that venv are not available from jupyterhub when that venv (kernel) is active.

To install venv:

python -m venv python_ai
sudo python -m ipykernel install --name=python_ai

To install package:

source python_ai/bin/activate
pip install actionrules-lukassykora 

Package is installed and available when Python is run from venv in terminal:

source python_ai/bin/activate
python
from actionrules.actionRulesDiscovery import ActionRulesDiscovery

However, when I use the python_ai kernel from JupyterHub, the same python import statement returns Not found error:

It looks like the venv is registered with jupyterhub but jupyterhub does not know that is should look for packages in the venv directory.

To support this: when packages are installed from Jupyter Hub (web interface) they are placed to
['/opt/tljh/user/lib/python3.9/site-packages'] as determined from the UI using import site; site.getsitepackages()

However, when one is in the activated virtual environment accessed through the terminal, the result of import site; site.getsitepackages() is: ['/home/jupyter-...@.../python_ai/lib/python3.9/site-packages']
Which is different from what is shown when the same command is executed from the python_ai kernel in jupyterhub UI (as shown on the screenshot above).
The contents of usr/local/share/jupyter/kernels/python_ai/kernel.json is

> {
>  "argv": [
>   "/opt/tljh/user/bin/python",
>   "-m",
>   "ipykernel_launcher",
>   "-f",
>   "{connection_file}"
>  ],
>  "display_name": "python_ai",
>  "language": "python",
>  "metadata": {
>   "debugger": true
>  }

I tried to include as much information as possible, but I am not sure I am on the right path.
Any help is appreciated.

You’ve creating a new venv, but you haven’t activated it, so you’re still using the default Python environment.
https://ipython.readthedocs.io/en/latest/install/kernel_install.html

In addition sudo will ignore part of your environment, so you’d need to activate it after switching to a root shell.

Thanks manics for your feedback.

I actually activated it prior to package install with

source python_ai/bin/activate

This was successful as the prompt looked like this after the source command was run:

(python_ai) jupyter-...@...@jupyter:~$

I am not sure about the sudo part. Running python -m ipykernel install with sudo was I think necessary.

I can run python -m ipykernel install --name=python_ai --user without sudo. But this will install kernelspec to `/home/jupyter-myusername@mydomain/.local/share/jupyter/kernels/python_ai’, which is probably no good for a site-wide installation of the kernel/venv (?).

Running without --user requires sudo as otherwise I get Permission denied: 'logo-32x32.png'. With sudo, it gets installed to /usr/local/share/jupyter/kernels/python_ai, which appears to be site-wide as desired and jupyterhub actually sees this and shows the python_ai kernel option.

A copy of this discussion post with additional details has been posted as an issue on jupyterhub github: Kernel corresponding to a venv does not contain packages installed in the venv · Issue #4474 · jupyterhub/jupyterhub · GitHub

The key is this bit:

>  "argv": [
>   "/opt/tljh/user/bin/python",

where the env is not using the Python you specified. As @manics mentioned:

sudo will ignore part of your environment, so you’d need to activate it after switching to a root shell.

so python executed by sudo is not the python executable you are expecting in the env.

Alternately, you can pass absolute paths to the executable to make sure sudo’s really doing what you want. You don’t actually have to activate the env, the absolute path should suffice:

sudo ./python_ai/bin/python -m ipykernel install --name=python_ai

Or as I often do, if you’ve activated an env and need to use it with sudo, resolve the absolute path with which before passing it to sudo:

sudo $(which python) -m ipykernel ...

(there’s also sudo -E to preserve the calling environment. Lots of ways!)

Thanks for the valuable pointers, it will probably have to do with python location.
However, no matter if I run

sudo python -m ipykernel install --name=python_ai
or
sudo $(which python) -m ipykernel install --name=python_ai

The generated kernel.json always contains:

“/opt/tljh/user/bin/python”,

Also, when I am in the terminal in the activated venv, which python returns path to python executable within the venv, e.g. /home/jupyter-.../python_ai/bin/python

I tried to manually change the location in kernel.json to the same location as returned by which executed from venv, but then the kernel does not work properly.

This would be an argument for an issue with Python executable location.
However,

ls -l /opt/tljh/user/bin/python
returns
lrwxrwxrwx 1 root root 9 Sep 3 2022 /opt/tljh/user/bin/python -> python3.9

while
ls -l /home/jupyter-.../python_ai/bin/python
returns
lrwxrwxrwx 1 myusername myusername 25 Jun 8 08:31 /home/myusername/python_ai/bin/python ->/opt/tljh/user/bin/python

which python3.9
returns
/opt/tljh/user/bin/python3.9

So both python executables appear to eventually point at /opt/tljh/user/bin/python3.9.

hm, I’ve never seen an absolute path to a venv executable produce a different path than itself.

Does it really not work (i.e. the path is still tljh/user) if you do:

sudo ./python_ai/bin/python -m ipykernel install --name=python_ai

? That would mean some behavior that’s very different from the standard library venv package, or the wrong files are being checked for results.

When you manually set the path and say it ‘does not work properly’, what does that mean? The env must be readable and executable by all users, and you gave a hint that the env is in /home/jupyter-.... A shared env generally needs to be world-readable and executable, including all of its parent directories.

Thanks for your patience and help minrk and manics. It now works.

The complete script is

python -m venv python_aix
source python_aix/bin/activate
pip install ipykernel
sudo ./python_aix/bin/python -m ipykernel install --name=python_aix

The ipykernel had to be installed again within the newly created venv first.

This places the correct path in kernel.json (/home/username/python_aix/bin/python) and sets the permissions.

Any packages installed in the python_aix venv are visible from jupyterhub UI kernel.