Skip to main content

A toolkit for reproducible Jupyter notebooks, powered by uv.

Project description

juv

version license python versions Actions status

A toolkit for reproducible Jupyter notebooks, powered by uv.

Features

  • 🗂️ Create, manage, and run reproducible notebooks
  • 📌 Pin dependencies with PEP 723 - inline script metadata
  • 🚀 Launch ephemeral sessions for multiple front ends (e.g., JupyterLab, Notebook, NbClassic)
  • ⚡ Powered by uv for fast dependency management

Installation

juv is published to the Python Package Index (PyPI) and can be installed globally with uv or pipx (recommended):

uv tool install juv
# or pipx install juv

You can also use the uvx command to invoke it without installing:

uvx juv

Usage

juv should feel familar for uv users. The goal is to extend its dependencies management to Jupyter notebooks.

# create a notebook
juv init notebook.ipynb
juv init --python=3.9 notebook.ipynb # specify a minimum Python version

# add dependencies to the notebook
juv add notebook.ipynb pandas numpy
juv add notebook.ipynb --requirements=requirements.txt

# launch the notebook
juv run notebook.ipynb
juv run --with=polars notebook.ipynb # additional dependencies for this session (not saved)
juv run --jupyter=notebook@6.4.0 notebook.ipynb # pick a specific Jupyter frontend
juv run --jupyter=nbclassic notebook.ipynb -- --no-browser # pass additional arguments to Jupyter

# JUV_JUPYTER env var to set preferred Jupyter frontend (default: lab)
export JUV_JUPYTER=nbclassic
juv run notebook.ipynb

If a script is provided to run, it will be converted to a notebook before launching the Jupyter session.

uvx juv run script.py
# Converted script to notebook `script.ipynb`
# Launching Jupyter session...

Motivation

Rethinking the "getting started" guide for notebooks

Jupyter notebooks are the de facto standard for data science, yet they suffer from a reproducibility crisis.

This issue does not stem from a fundamental lack of care for reproducibility. Rather, our tools limit us from easily falling into the pit of success with reproducible notebooks - in particular, managing dependencies.

Notebooks are much like one-off Python scripts and therefore do not benefit from the same dependency management as packages. Being a "good steward" of notebooks in this context requires discipline (due to the manual nature of virtual environments) and knowledge of Python packaging - a somewhat unreasonable expectation for domain experts who are focused on solving problems, not software engineering.

You will often find a "getting started" guide in the wild like this:

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt # or just pip install pandas numpy, etc
jupyter lab

Four lines of code, where a few things can go wrong. What version of Python? What package version(s)? What if we forget to activate the virtual environment?

The gold standard for a "getting started" guide is a single command (i.e, no guide).

<magic tool> run notebook.ipynb

However, this gold standard has long been out of reach for Jupyter notebooks. Why?

First, virtual environments are a leaky abstraction and deeply ingrained in the Python psyche: create, activate, install, run. Their historical "cost" has forced us to treat them as entities that must be managed explicitly. In fact, an entire ecosystem of tooling and best practices are oriented around supporting long-lived environments, rather than something more ephemeral. End users separately create and then mutate virtual environments with low-level tools like pip. The manual nature and overhead of these steps encourages sharing environments across projects.

Second, only Python packages could historically specify their dependencies. Lots of data science code lives in notebooks, not packages, and there has not been a way to specify dependencies for standalone scripts without external files (e.g., requirements.txt).

Aligning of the stars

Two key ideas have changed my perspective on this problem and inspired juv:

  • Virtual environments are now "cheap". If you'd asked me a year ago, I would have said virtual environments were a necessary evil. uv is such a departure from the status quo that it forces us to rethink best practices. Environments are now created faster than JupyterLab starts - why keep them around at all?

  • PEP 723. Inline script metadata introduces a standard way to specify dependencies in standalone Python scripts. A single file can now contain everything needed to run it, without relying on external files like requirements.txt or pyproject.toml.

So, what if:

  • Environments were disposable by default?
  • Notebooks could specify their own dependencies?

This is the vision of juv

[!NOTE] Dependency management is just one challenge for notebook reproducibility (non-linear execution being another). juv aims to solve this specific pain point for the existing ecosystem. I'm personally excited for initiatives that rethink notebooks from the ground up and make a tool like juv obsolete.

How

PEP 723 (inline script metadata) allows specifying dependencies as comments within Python scripts, enabling self-contained, reproducible execution. This feature could significantly improve reproducibility in the data science ecosystem, since many analyses are shared as standalone code (not packages). However, a lot of data science code lives in notebooks (.ipynb files), not Python scripts (.py files).

juv bridges this gap by:

  • Extending PEP 723-style metadata support from uv to Jupyter notebooks
  • Launching Jupyter sessions for various notebook front ends (e.g., JupyterLab, Notebook, NbClassic) with the specified dependencies

It's a simple Python script that parses the notebook and starts a Jupyter session with the specified dependencies (piggybacking on uv's existing functionality).

Alternatives

juv is opinionated and might not suit your preferences. That's ok! uv is super extensible, and I recommend reading the wonderful documentation to learn about its primitives.

For example, you can achieve a similar workflow using the --with-requirements flag:

uvx --with-requirements=requirements.txt --from=jupyter-core --with=jupyterlab jupyter lab notebook.ipynb

While slightly more verbose and breaking self-containment, this approach totally works and saves you from installing another dependency.

Contributing

juv welcomes contributions in the form of bug reports, feature requests, and pull requests. See the CONTRIBUTING.md for more information.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

juv-0.2.16.tar.gz (49.4 kB view details)

Uploaded Source

Built Distribution

juv-0.2.16-py3-none-any.whl (19.6 kB view details)

Uploaded Python 3

File details

Details for the file juv-0.2.16.tar.gz.

File metadata

  • Download URL: juv-0.2.16.tar.gz
  • Upload date:
  • Size: 49.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for juv-0.2.16.tar.gz
Algorithm Hash digest
SHA256 a2ae1ed39342e1cc159fa6dbd2a0641c7d5506b1d85c5e78c1b1ac8966fe99f7
MD5 7bfd043c30e0bddfbffcebed98db8a2b
BLAKE2b-256 3d82f6fb9eecf060dd8db706ca620392284caa36e308af603b519c3852718d89

See more details on using hashes here.

Provenance

The following attestation bundles were made for juv-0.2.16.tar.gz:

Publisher: release.yml on manzt/juv

Attestations:

File details

Details for the file juv-0.2.16-py3-none-any.whl.

File metadata

  • Download URL: juv-0.2.16-py3-none-any.whl
  • Upload date:
  • Size: 19.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for juv-0.2.16-py3-none-any.whl
Algorithm Hash digest
SHA256 e9e34547804dc25442e0e06bcbda1a7f38b3fa4ed1cd42b4ebdd9b1a5ad13108
MD5 786f67408813b55734084b9f30bc5bdd
BLAKE2b-256 5ba3793e1a3c424860f29b93f25a204504514ee01dcf4e9bfa5dbde74a88c924

See more details on using hashes here.

Provenance

The following attestation bundles were made for juv-0.2.16-py3-none-any.whl:

Publisher: release.yml on manzt/juv

Attestations:

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page