Skip to content

Python notebook environments with uv

Use a separate Python environment for an analysis when its notebooks need their own package versions. The uv prototype creates an environment in your analysis folder and registers it as a kernel you can select in JupyterLab. Several notebooks in the same folder can share that environment.

Open a Terminal in Neurodesktop’s JupyterLab and run the commands as your normal notebook user, without sudo. You need a recent Neurodesktop image with uv, the image’s Python, and the Neurodesk kernel wrapper:

Terminal window
uv --version
/opt/conda/bin/python -c 'import tomllib, jupyter_core'
test -x /opt/neurodesktop/kernel_wrapper.sh

If a check fails, use a newer Neurodesktop image before trying the prototype. The helper requires Python 3.11 or newer. Initial setup and installing new packages need access to the package index.

Choose a folder on persistent storage. Use an analysis subfolder, such as ~/analyses/demo, rather than your home directory or the root of a mounted drive. The prototype refuses existing unmanaged .venv directories and projects inside uv workspaces; start with a fresh analysis folder for your first trial.

Download the helper from the tested recovery-fix commit (Neurodesktop PR #1263):

Terminal window
mkdir -p "$HOME/.local/share/neurodesk"
curl --fail --location \
https://raw.githubusercontent.com/neurodesk/neurodesktop/587c607c7d4f10727b95bfe118f26197bf9c5917/config/jupyter/uv_kernel_prototype.py \
--output "$HOME/.local/share/neurodesk/uv-kernel-prototype.py"

Keep this file at the same path while using its kernels. Each registration refers to this helper and the image’s Python interpreter.

Create an environment and select its kernel

Section titled “Create an environment and select its kernel”
Terminal window
mkdir -p "$HOME/analyses/demo"
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
init "$HOME/analyses/demo"

This creates or updates:

  • pyproject.toml, which declares the project’s dependencies.
  • uv.lock, which records the resolved package versions.
  • .venv, which contains the project’s Python environment.
  • .neurodesk_uv_prototype.json, a local ownership record beside .venv.
  • A user kernel registration named Python (uv prototype: demo).

The helper records ipykernel as a development dependency. A new project uses version 6.31.0, the compatibility version selected for this prototype. Running init again reuses the registration rather than creating a duplicate. Existing projects without uv.lock get a lockfile, including projects that already declare ipykernel.

Open or create a notebook in the analysis folder. Click its kernel name and select Python (uv prototype: demo). If it is not listed, reload JupyterLab and reopen the kernel selector. Confirm the environment from a notebook cell:

import sys
print(sys.executable)

The path should end in analyses/demo/.venv/bin/python. Selecting a kernel does not move a notebook into that folder; save related notebooks alongside the project files.

In the JupyterLab terminal, run:

Terminal window
uv add --project "$HOME/analyses/demo" numpy nibabel

Restart the notebook kernel after changing packages. You can then import them in a cell:

import numpy as np
import nibabel as nib

Use uv add to keep dependency changes in pyproject.toml and uv.lock. The prototype does not install pip by default, so %pip install is not its supported package-management workflow. Ad hoc installations would not update the lockfile.

Packages from Neurodesktop’s default Python environment are not automatically available in this environment. For interactive widgets, add their Python packages (for example, uv add --project "$HOME/analyses/demo" ipywidgets). Neurodesk’s kernel wrapper still initializes its tool/module paths when the kernel starts. Use Conda or Neurodesk containers when your workflow needs software distributed through those systems.

Keep your notebooks, pyproject.toml, and uv.lock together, and commit the latter two files if you use Git. Do not commit .venv; it can be recreated and is tied to its filesystem location. Keep .neurodesk_uv_prototype.json with the folder when moving it. This record contains a local absolute path; keep it out of version control. It lives outside .venv so uv can recreate the environment without losing ownership information.

Kernel registration is local to your Neurodesktop user and is not carried by the notebook or lockfile.

Preserve the helper and Jupyter’s user data directory across container replacement, as well as your project folder. The default Jupyter data location is ~/.local/share/jupyter; installations can override it. Keeping uv’s cache and any uv-managed Python installations persistent also avoids repeated downloads.

Kernel startup runs the prepared environment directly. It does not download packages, synchronize dependencies, or update the lockfile. After manually editing dependencies, synchronize explicitly before restarting your kernel:

Terminal window
uv sync --project "$HOME/analyses/demo" --locked --group dev

--locked fails if the manifest and lockfile disagree. Prefer uv add for normal package changes so both files stay in step.

If you installed the original helper, repeat the download command above to replace it at the same path. Shut down affected notebook kernels, then run init on each project at its current location to migrate the ownership record:

Terminal window
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
init "$HOME/analyses/demo"

If uv recreated .venv after an interpreter change and the original helper now reports Existing .venv is unmanaged, the updated helper can recover using the project’s existing kernel registration. Run rebuild with the updated helper at the original project path before moving the folder or removing its registration:

Terminal window
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
rebuild "$HOME/analyses/demo"

Recovery requires either a prototype ownership record or a matching registration, and the existing environment must be uv-created. The helper continues to refuse unmanaged and symlinked environments. Do not remove ownership records as a troubleshooting step.

To recreate the environment from its existing manifest and lockfile:

Terminal window
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
rebuild "$HOME/analyses/demo"

A rebuild needs both pyproject.toml and uv.lock. It can work offline if the required Python interpreter and packages are already available locally; a lockfile alone does not contain those packages.

After moving the folder, rebuild at the new path and remove the old kernel registration. For example, after moving demo to renamed-demo:

Terminal window
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
rebuild "$HOME/analyses/renamed-demo"
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
remove "$HOME/analyses/demo"

Select Python (uv prototype: renamed-demo) in each affected notebook. Old registrations continue to refer to the old path until removed.

Terminal window
/opt/conda/bin/python "$HOME/.local/share/neurodesk/uv-kernel-prototype.py" \
remove "$HOME/analyses/demo"

If no registration matches the path, the command reports No prototype registration and exits with status 1. Check the old path after a move rather than assuming its kernel was removed.

A successful removal deletes only the kernel registration. It retains notebooks, dependency files, and .venv. Switch affected notebooks to another kernel. If you no longer use any prototype kernels, you can also remove the downloaded helper.