Skip to main content

Generate exercise versions of Jupyter notebooks

Project description

ipynb-scrubber

Generate exercise versions of Jupyter notebooks by clearing solution cells and removing instructor-only content.

[!NOTE] This is a project made to satisfy a need on some personal projects. The behaivor has been tested to work for these projects but will not be supported for other uses.

Issues will be reviewed if opened, and any legitimate bugs will be fixed, but new features or ideas will likely be rejected unless accompanied by a working pull request with comprehensive tests.

Thanks for understanding.

Features

  • Clear solution cells: Replace cell contents with placeholder text while preserving structure
  • Custom replacement text: Use cell-specific text instead of default placeholder
  • All cell types supported: Works with code, markdown, and raw cells
  • Remove cells entirely: Omit instructor-only cells from the output
  • Multiple syntax options: Use cell tags or cell-type-appropriate comment syntax
  • Preserve structure: Maintain notebook structure and metadata
  • Clear all outputs: Remove all cell outputs and execution counts for a clean slate
  • Project-wide processing: Process multiple notebooks with a single command using a TOML config file
  • Flexible CLI: Unix-style stdin/stdout for single files, or config-based batch processing for projects

Installation

Install with a python package manager like pip or uv:

pip install ipynb-scrubber

Usage

The tool provides two commands for different workflows:

Single Notebook: scrub-notebook

Process a single notebook via stdin/stdout (Unix-style):

ipynb-scrubber scrub-notebook < input.ipynb > output.ipynb

Options

  • --clear-tag TAG: Tag marking cells to clear (default: scrub-clear)
  • --clear-text TEXT: Replacement text for cleared cells where unspecified (default: # TODO: Implement this)
  • --omit-tag TAG: Tag marking cells to omit entirely (default: scrub-omit)

Examples

Using default settings:

ipynb-scrubber scrub-notebook < lecture.ipynb > exercise.ipynb

Using custom tags:

ipynb-scrubber scrub-notebook \
    --clear-tag solution \
    --omit-tag private \
    < lecture.ipynb > exercise.ipynb

Using custom placeholder text:

ipynb-scrubber scrub-notebook \
    --clear-text "# YOUR CODE HERE" \
    < lecture.ipynb > exercise.ipynb

Project-Wide: scrub-project

Process multiple notebooks using a configuration file:

ipynb-scrubber scrub-project

The command searches for configuration in the following order, starting from the current directory and moving upward:

  1. .ipynb-scrubber.toml (standalone config file)
  2. pyproject.toml with [tool.ipynb-scrubber] section

This means you can run the command from any subdirectory of your project.

Configuration File Formats

Option 1: Standalone .ipynb-scrubber.toml

Create a .ipynb-scrubber.toml file with global options and file entries:

# Global options (optional - these are defaults)
[options]
clear-tag = "scrub-clear"
clear-text = "# TODO: Implement this"
omit-tag = "scrub-omit"

# File entries (required - at least one)
[[files]]
input = "lectures/lesson1.ipynb"
output = "exercises/lesson1.ipynb"

[[files]]
input = "lectures/lesson2.ipynb"
output = "exercises/lesson2.ipynb"
clear-text = "# YOUR CODE HERE"  # Override global option

[[files]]
input = "lectures/lesson3.ipynb"
output = "exercises/lesson3.ipynb"
clear-tag = "solution"  # Custom tag for this file
omit-tag = "instructor"

Each file entry supports:

  • input (required): Path to source notebook
  • output (required): Path where scrubbed notebook will be written
  • clear-tag (optional): Override global clear tag
  • clear-text (optional): Override global clear text
  • omit-tag (optional): Override global omit tag

Option 2: Using pyproject.toml

Add configuration to your existing pyproject.toml under [tool.ipynb-scrubber]:

# Global options (optional - these are defaults)
[tool.ipynb-scrubber.options]
clear-tag = "scrub-clear"
clear-text = "# TODO: Implement this"
omit-tag = "scrub-omit"

# File entries (required - at least one)
[[tool.ipynb-scrubber.files]]
input = "lectures/lesson1.ipynb"
output = "exercises/lesson1.ipynb"

[[tool.ipynb-scrubber.files]]
input = "lectures/lesson2.ipynb"
output = "exercises/lesson2.ipynb"
clear-text = "# YOUR CODE HERE"

This is convenient if you're already using pyproject.toml for your Python project. The tool will automatically find and use this configuration.

Custom Config File

Specify a different config file location (bypasses automatic discovery):

ipynb-scrubber scrub-project --config-file path/to/config.toml

Marking Cells

There are two ways to mark cells for processing:

1. Cell Tags (All Cell Types)

Add tags to cells using Jupyter's tag interface. This works for all cell types (code, markdown, raw):

  • Add scrub-clear tag to solution cells that should be cleared
  • Add scrub-omit tag to cells that should be removed entirely

2. Source-Based Options (Code & Markdown)

Use cell-type-appropriate syntax for more control, including custom replacement text:

Code Cells - Quarto Options

#| scrub-clear
def secret_solution():
    return 42

# Or with custom replacement text:
#| scrub-clear: # WRITE YOUR SOLUTION HERE
def another_solution():
    return "hidden"

# To omit entirely:
#| scrub-omit
# This cell will be removed
print("Instructor only!")

Markdown Cells - HTML Comments

<!-- scrub-clear -->
## Answer

The solution is 42 because...

<!-- Or with custom replacement text: -->
<!-- scrub-clear: **Write your answer here** -->
## Another Question

This answer will be replaced.

<!-- To omit entirely: -->
<!-- scrub-omit -->
## Instructor Notes

These notes are only for the instructor.

Raw Cells - Tags Only

Raw cells only support metadata tags to avoid format conflicts:

# Cell metadata: {"tags": ["scrub-clear"]}
$$\int_0^1 x^2 dx = \frac{1}{3}$$

# Cell metadata: {"tags": ["scrub-omit"]}
% This LaTeX comment will be omitted entirely

Custom Replacement Text

When using source-based options, you can specify custom text to replace the cleared content:

  • #| scrub-clear: Your custom text (code cells)
  • <!-- scrub-clear: Your custom text --> (markdown cells)
  • Empty text: #| scrub-clear: (results in empty cell)

If no custom text is provided, the default --clear-text value is used.

Example

Input Notebook

Code Cell 1 (no tags):

# Instructions - this will remain unchanged
print("Exercise: implement the functions below")

Code Cell 2 (Quarto option with custom text):

#| scrub-clear: # TODO: Write your add function here
def add(a, b):
    return a + b

result = add(1, 2)
print(f"Result: {result}")

Markdown Cell 3 (HTML comment):

<!-- scrub-clear: **Write your explanation here** -->
## Solution Explanation

The add function works by using the + operator...

Code Cell 4 (cell tag - will be omitted):

# Cell has metadata: {"tags": ["scrub-omit"]}
# This cell will be removed entirely
assert add(1, 2) == 3
print("Tests pass!")

Output Notebook

Code Cell 1 (unchanged):

# Instructions - this will remain unchanged
print("Exercise: implement the functions below")

Code Cell 2 (cleared with custom text):

# TODO: Write your add function here

Markdown Cell 3 (cleared with custom text):

**Write your explanation here**

Code Cell 4 (omitted entirely)

Behavior

  • All cell outputs are cleared: Every cell has its output and execution count removed
  • Tagged cells are processed:
    • Cells with the clear tag have their source code replaced with placeholder text
    • Cells with the omit tag are removed entirely from the output
  • Notebook metadata: An exercise_version flag is added to the notebook metadata
  • Error handling: Invalid notebooks produce helpful error messages

License

Apache License 2.0

Contributing

Contributions are welcome! Please feel free to submit a Pull Request, but note that comprehensive test coverage and clear justification for why the request should be considered (keeping in mind new features increase the maintenance burden) must be included.

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

ipynb_scrubber-0.3.0.tar.gz (39.4 kB view details)

Uploaded Source

Built Distribution

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

ipynb_scrubber-0.3.0-py3-none-any.whl (16.2 kB view details)

Uploaded Python 3

File details

Details for the file ipynb_scrubber-0.3.0.tar.gz.

File metadata

  • Download URL: ipynb_scrubber-0.3.0.tar.gz
  • Upload date:
  • Size: 39.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ipynb_scrubber-0.3.0.tar.gz
Algorithm Hash digest
SHA256 f7ce67df10563c7defabba2f38c8a96f4f19f81651e8f3766cb6d8c1beea3ebe
MD5 b755c268f5d2204b7e05d9d986b66c96
BLAKE2b-256 eb1cc3d8738d214640cd0f5b689298722de36f8e6cccbb3ab12f1e27929e4e65

See more details on using hashes here.

Provenance

The following attestation bundles were made for ipynb_scrubber-0.3.0.tar.gz:

Publisher: release.yml on jkeifer/ipynb-scrubber

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ipynb_scrubber-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: ipynb_scrubber-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 16.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ipynb_scrubber-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4972fb5e77e88cc5224880f14021b3e48f347e1173df453a10d4db489260edaf
MD5 d610c68bc1b166ff00957a830c9d76b6
BLAKE2b-256 b690c5dc3b241cdea13639776cca5137a1684d843e734a4eb2067fa45ede8247

See more details on using hashes here.

Provenance

The following attestation bundles were made for ipynb_scrubber-0.3.0-py3-none-any.whl:

Publisher: release.yml on jkeifer/ipynb-scrubber

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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