Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
dd09e76
init
selmanozleyen Jun 30, 2025
40a92e2
add yaml file
selmanozleyen Jun 30, 2025
ee40db6
fix tox ini file
selmanozleyen Jun 30, 2025
460d659
update tox deps
selmanozleyen Jun 30, 2025
dd1d60a
since the notebooks work add others to test
selmanozleyen Jun 30, 2025
77c7eca
remove the tutorials folder as it has too much dependencies
selmanozleyen Jun 30, 2025
3ada541
Merge branch 'main' into feature/test-notebooks-ci
selmanozleyen Aug 20, 2025
8da3ed9
convert to python
selmanozleyen Aug 21, 2025
41bff5e
use uv and hatch for notebooks only
selmanozleyen Aug 21, 2025
fd5461f
need to add notebook dependencies now
selmanozleyen Jun 30, 2025
88b32c6
add yaml file
selmanozleyen Jun 30, 2025
250c0c9
fix tox ini file
selmanozleyen Jun 30, 2025
bb01800
update tox deps
selmanozleyen Jun 30, 2025
f46277b
since the notebooks work add others to test
selmanozleyen Jun 30, 2025
b545067
remove the tutorials folder as it has too much dependencies
selmanozleyen Jun 30, 2025
7e49bd5
convert to python
selmanozleyen Aug 21, 2025
9ce411c
update with new rebase
selmanozleyen Sep 4, 2025
4f53eb6
Merge branch 'feature/test-notebooks-ci' of https://github.com/selman…
selmanozleyen Sep 4, 2025
2ce1318
specify path
selmanozleyen Sep 4, 2025
8ad34c5
Merge branch 'main' into feature/test-notebooks-ci
selmanozleyen Sep 4, 2025
8236ab5
remove .run_notebooks
selmanozleyen Sep 4, 2025
496efb1
Merge branch 'feature/test-notebooks-ci' of https://github.com/selman…
selmanozleyen Sep 4, 2025
46f0ef4
no need to specify uv anymore
selmanozleyen Sep 4, 2025
1afa96b
remove toxini and make sure pyproject is same as main
selmanozleyen Sep 4, 2025
1ca5718
add step to setup squidpy kernel
selmanozleyen Sep 4, 2025
6ecab08
fix syntax err
selmanozleyen Sep 4, 2025
bef64d3
update the notebook commit
selmanozleyen Oct 1, 2025
3a9a004
update the nb commit for rendering
selmanozleyen Oct 1, 2025
494e38b
Merge branch 'main' into feature/test-notebooks-ci
selmanozleyen Oct 1, 2025
f474058
update the home page to add new section
selmanozleyen Oct 1, 2025
fd6f9a4
Merge branch 'feature/test-notebooks-ci' of https://github.com/selman…
selmanozleyen Oct 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/test-notebooks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Test notebooks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reproducibility, should we have a check for whether the notebooks change? This would basically turn the notebooks into tests-for-stability, but could be a nice check. @timtreis or @LucaMarconato have you guys ever received feedback about stability?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could have expected and output folders to compare but we'd have to update expected when we expect/want the output to change. Similar to plot unit tests. I think this is doable but would fit better as a separate issue imo


on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python: ["3.11"]

steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}

- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
python-version: ${{ matrix.python }}
cache-dependency-glob: pyproject.toml
- name: Create notebooks environment
run: uvx hatch -v env create notebooks
- name: Test notebooks
env:
MPLBACKEND: agg
PLATFORM: ${{ matrix.os }}
DISPLAY: :42

run: |
uvx hatch run notebooks:setup-squidpy-kernel
uvx hatch run notebooks:run-notebooks
96 changes: 96 additions & 0 deletions .scripts/ci/run_notebooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
from __future__ import annotations

import argparse
import glob
import os
import subprocess
import sys

EPILOG = """
Examples:
python run_notebooks.py docs/notebooks
python run_notebooks.py /path/to/notebooks --kernel my-kernel
"""


def main() -> None:
# Set up argument parser
parser = argparse.ArgumentParser(
description="Run Jupyter notebooks in specified directories using jupytext",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=EPILOG,
)

parser.add_argument("base_directory", help="Base directory containing notebook subdirectories")

parser.add_argument(
"-k", "--kernel", default="squidpy", help="Jupyter kernel to use for execution (default: squidpy)"
)

parser.add_argument(
"--dry-run", action="store_true", help="Show which notebooks would be run without executing them"
)

args = parser.parse_args()

# Base directory for notebooks
base_dir = args.base_directory

# Define notebook directories or patterns
notebook_patterns = [
f"{base_dir}/examples/tools/*.ipynb",
f"{base_dir}/examples/plotting/*.ipynb",
f"{base_dir}/examples/image/*.ipynb",
f"{base_dir}/examples/graph/*.ipynb",
# f"{base_dir}/tutorials/*.ipynb" # don't include because it contains many external modules
]

# Initialize a list to hold valid notebook paths
valid_notebooks = []

# Gather all valid notebook files from the patterns
print("Gathering notebooks...")
for pattern in notebook_patterns:
for nb_path in glob.glob(pattern):
if os.path.isfile(nb_path): # Check if the file exists
valid_notebooks.append(nb_path) # Add to the list of valid notebooks

# Check if we have any notebooks to run
if len(valid_notebooks) == 0:
print("No notebooks found to run.")
sys.exit(1)

# Echo the notebooks that will be run for clarity
print("Preparing to run the following notebooks:")
for nb in valid_notebooks:
print(f" {nb}")

# If dry run, exit here
if args.dry_run:
print(f"\nDry run complete. Would execute {len(valid_notebooks)} notebooks with kernel '{args.kernel}'.")
return

# Initialize a flag to track the success of all commands
all_success = True

# Execute all valid notebooks
print(f"\nExecuting notebooks with kernel '{args.kernel}'...")
for nb in valid_notebooks:
print(f"Running {nb}")
try:
subprocess.run(["jupytext", "-k", args.kernel, "--execute", nb], check=True)
except subprocess.CalledProcessError:
print(f"Failed to run {nb}")
all_success = False

# Check if any executions failed
if not all_success:
print("One or more notebooks failed to execute.")
sys.exit(1)

print("All notebooks executed successfully.")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ We are happy about any contributions! Before you start, check out our `contribut

notebooks/tutorials/index
notebooks/examples/index
notebooks/deprecated_features/index

.. |PyPI| image:: https://img.shields.io/pypi/v/squidpy.svg
:target: https://pypi.org/project/squidpy/
Expand Down
18 changes: 17 additions & 1 deletion hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,20 @@ python = [ "3.13" ]
# set the environment variable `UV_PRERELEASE` to "allow".
matrix.deps.env-vars = [
{ key = "UV_PRERELEASE", value = "allow", if = [ "pre" ] },
]
]

[envs.notebooks]
extra-dependencies = [
"ipykernel",
"jupytext",
"nbconvert",
"leidenalg",
"watermark",
"napari-spatialdata",
]
extras = ["docs"]

[envs.notebooks.scripts]

setup-squidpy-kernel = "python -m ipykernel install --user --name=squidpy"
run-notebooks = "python ./.scripts/ci/run_notebooks.py docs/notebooks"
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,3 @@ exclude_lines = [
show_missing = true
precision = 2
skip_empty = true
sort = "Miss"