Skip to main content

Programmatic Jupyter cell execution with persistent kernels

Project description

jupyter-cli

PyPI version Python 3.8+ License: MIT

Programmatic Jupyter notebook cell execution with persistent kernels.

The Problem

Existing Jupyter tools don't support selective cell execution with persistent state:

  • papermill/nbconvert/nbexec: Run entire notebooks
  • jupyter console: Requires manual interaction
  • nbclient: Kernel dies when Python process exits

jupyter-cli fills this gap: execute specific cells while keeping kernel state alive between CLI invocations.

Use Case

A data science notebook with ~60 cells:

  • Cells 1-49: Expensive setup (DB queries, model training) - takes ~20 minutes
  • Cells 50+: Experimental iterations

With jupyter-cli:

jupyter-cli start notebook.ipynb
jupyter-cli exec notebook.ipynb 0 1 2 ... 49  # Run setup once
jupyter-cli exec notebook.ipynb 50            # Iterate on cell 50
jupyter-cli exec notebook.ipynb 50            # Again, without re-running setup
jupyter-cli stop notebook.ipynb

Installation

pip install jupyter-cli

# Or from source
git clone https://github.com/andrewting19/jupyter-cli.git
cd jupyter-cli
pip install -e .

Requirements:

  • Python 3.8+
  • jupyter_client
  • nbformat
  • click

Quick Start

# Start a persistent kernel
jupyter-cli start notebook.ipynb

# Execute cells (0-indexed)
jupyter-cli exec notebook.ipynb 0 1 2

# Run inline code (no cell needed)
jupyter-cli run notebook.ipynb "print(df.shape)"

# Stop the kernel
jupyter-cli stop notebook.ipynb

Commands

Kernel Management

Command Description
start <notebook> Start persistent kernel
stop <notebook> Stop kernel
stop --all Stop all kernels
status [notebook] Check kernel status

Cell Execution

Command Description
exec <notebook> <cells...> Execute specific cells
exec <notebook> --all Execute all code cells
exec <notebook> --range 0-10 Execute cell range
exec <notebook> 0 --quiet Suppress output

Inline Code Execution

Command Description
run <notebook> "code" Execute arbitrary code on kernel
run <notebook> - Read code from stdin
run <notebook> "code" --quiet Suppress output

Notebook Exploration

Command Description
list <notebook> List all cells with preview
list <notebook> --code Only code cells
list <notebook> --range 0-10 Cell range
read <notebook> 0 1 2 Read full cell source
read <notebook> --code Read all code cells
search <notebook> "pattern" Search cells
search <notebook> "regex" -r Regex search
outputs <notebook> 5 Read stored outputs
info <notebook> Notebook info

Examples

Exploring a Notebook

# Quick overview
$ jupyter-cli list notebook.ipynb
[0] code: import pandas as pd
[1] code: df = pd.read_csv('data.csv')
[2] code: df.head()
[3] markdown: ## Data Cleaning
[4] code: df = df.dropna()
...

# Find all cells using DataFrame
$ jupyter-cli search notebook.ipynb "DataFrame"
Found 3 match(es):
[5] code: df = pd.DataFrame(data)
[12] code: result = DataFrame.from_dict(...)
[45] code: final_df = pd.DataFrame(results)

# Read specific cells
$ jupyter-cli read notebook.ipynb 5 12
=== Cell 5 (code) ===
df = pd.DataFrame(data)
print(df.shape)

=== Cell 12 (code) ===
result = DataFrame.from_dict(processed_data)

Selective Execution

# Start kernel
$ jupyter-cli start analysis.ipynb
Kernel started. ID: abc123

# Run expensive setup
$ jupyter-cli exec analysis.ipynb 0 1 2 3 4 5
[Cell 0] Executing: import pandas as pd...
[Cell 1] Executing: df = load_data()...
Loading 1M rows from database...
...

# Iterate on experiment cell
$ jupyter-cli exec analysis.ipynb 50
[Cell 50] Executing: model.fit(X, y)...
Accuracy: 0.85

# Edit cell 50 in your editor, then re-run
$ jupyter-cli exec analysis.ipynb 50
[Cell 50] Executing: model.fit(X, y, epochs=100)...
Accuracy: 0.92

# Clean up
$ jupyter-cli stop analysis.ipynb
Kernel stopped.

Inline Code Execution

# After running setup cells, inspect variables without modifying notebook
$ jupyter-cli run analysis.ipynb "df.shape"
[inline] Executing: df.shape
(10000, 25)

$ jupyter-cli run analysis.ipynb "df.columns.tolist()"
[inline] Executing: df.columns.tolist()
['id', 'name', 'price', 'category', ...]

# Test expressions before adding to notebook
$ jupyter-cli run analysis.ipynb "df.groupby('category')['price'].mean()"
[inline] Executing: df.groupby('category')['price'].mean()
category
electronics    299.99
clothing        49.99
...

# Debug: see what's in scope
$ jupyter-cli run analysis.ipynb "list(locals().keys())"
[inline] Executing: list(locals().keys())
['df', 'model', 'X_train', 'y_train', ...]

Reading Previous Outputs

$ jupyter-cli outputs notebook.ipynb --range 10-15
=== Cell 10 output ===
[stdout] Training model...
[stdout] Epoch 1/10: loss=0.5

=== Cell 12 output ===
[result] 0.923456

=== Cell 14 output ===
[error] ValueError: invalid shape

Gap Analysis: CLI vs Manual Jupyter

Feature jupyter-cli Manual Jupyter UI
Cell execution Yes Yes
Persistent kernel Yes Yes
Execute specific cells Yes Yes
State persistence Yes Yes
stdout/stderr output Yes Yes
Rich output (HTML) Truncated Full rendering
Image output [Image: PNG] Displayed
Interactive widgets No Yes
Cell editing No (use external editor) Yes
Magic commands Yes (via kernel) Yes
Auto-complete No Yes
Inline plots No (shows placeholder) Yes
Syntax highlighting No Yes
Variable inspector No Extension available
Debugging No Yes
Multiple kernels Yes (one per notebook) Yes

Key takeaway: jupyter-cli is for programmatic automation, not interactive development. Use it when you need:

  • Scripted notebook execution
  • LLM agent integration
  • CI/CD pipelines
  • Selective cell re-execution without re-running setup

Architecture

┌─────────────┐     ┌──────────────────┐     ┌────────────┐
│  CLI Client │────▶│  Daemon Process  │────▶│   Kernel   │
└─────────────┘     └──────────────────┘     └────────────┘
      │                     │                       │
      │  connection.json    │  keeps alive          │  Python
      │◀────────────────────│                       │  process
      │                     │                       │
      └─────────────────────┴───────────────────────┘
  1. Daemon Process: Starts kernel, keeps it alive, writes connection file
  2. CLI Client: Reads notebook, connects to kernel via connection file, executes cells

For LLM Agents

See CLAUDE.md for a comprehensive guide on using jupyter-cli with LLM agents, including:

  • Token-efficient exploration strategies
  • Common workflows
  • Best practices

Claude Code Skill

If you're using Claude Code, you can install the jupyter-cli skill to teach Claude how to use this tool:

# Install the skill (interactive prompt for global vs local)
jupyter-cli install-skill

# Or specify directly:
jupyter-cli install-skill --global   # Available in all projects
jupyter-cli install-skill --local    # Current directory only

After installation, restart Claude Code. The skill will:

  • Automatically activate when working with Jupyter notebooks
  • Be available via /jupyter-cli command
  • Teach Claude the proper commands and best practices

Development

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Run specific test class
pytest tests/test_jupyter_cli.py::TestCLIExec -v

License

MIT

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

jupyter_cli-0.1.7.tar.gz (23.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jupyter_cli-0.1.7-py3-none-any.whl (24.1 kB view details)

Uploaded Python 3

File details

Details for the file jupyter_cli-0.1.7.tar.gz.

File metadata

  • Download URL: jupyter_cli-0.1.7.tar.gz
  • Upload date:
  • Size: 23.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for jupyter_cli-0.1.7.tar.gz
Algorithm Hash digest
SHA256 f695c252cb2e0c711314b9f1b47f221dc443607c812c04947cb631cb54ec1529
MD5 c19798505e4b5e5c95bc3512c5b838d1
BLAKE2b-256 a9c2e2c4b60356c0219362ec2a20633472c5596cdcc2d1d6ff4cd5cae92f1300

See more details on using hashes here.

File details

Details for the file jupyter_cli-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: jupyter_cli-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 24.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for jupyter_cli-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 3c312f29aab916f72a9ac717bc9dd1cfca5e007f527faac5ced7255a80f89970
MD5 399a55659a0bdb5340cadc163f3515ae
BLAKE2b-256 fba39144d0076d354b59af111922e15c6481eb80a728c858722b53054703786b

See more details on using hashes here.

Supported by

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