Skip to content

Commit 39f6176

Browse files
committed
Add GitHub Action to generate .py mirrors from notebooks (jupytext)
Per Peter Norvig's suggestion on #1340 ('use a github action to generate a .py file from a notebook every time the notebook is edited'): - .github/workflows/notebooks-to-py.yml: on every push to master that touches a notebook, regenerate a readable py:percent mirror of each notebooks/**/*.ipynb with jupytext and commit the result (also prunes mirrors of deleted notebooks). One-way only — it never touches the hand-maintained aima/ package. Loop-safe (the commit changes only *.py, which doesn't match the .ipynb path trigger; plus [skip ci]). - Committed the initial .py mirrors for all notebooks so the repo starts in sync. - README/CONTRIBUTING: document that the .ipynb is the source of truth and the .py mirror is generated (don't hand-edit it); also fixed the stale bootstrap description (now '%run bootstrap.ipynb', no chdir).
1 parent 58f003c commit 39f6176

58 files changed

Lines changed: 19417 additions & 2 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Notebooks → .py
2+
3+
# Per Peter Norvig's suggestion (PR #1340): the .ipynb notebooks are the source
4+
# of truth; this action regenerates a readable, diffable .py mirror of each one
5+
# (via jupytext, py:percent) every time a notebook is edited on master, and
6+
# commits the result. It only writes notebooks/**/*.py — it never touches the
7+
# aima/ package, which is maintained by hand.
8+
9+
on:
10+
push:
11+
branches: [master]
12+
paths:
13+
- 'notebooks/**/*.ipynb'
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: write
18+
19+
# only one regeneration at a time; newer pushes supersede in-flight ones
20+
concurrency:
21+
group: notebooks-to-py
22+
cancel-in-progress: true
23+
24+
jobs:
25+
jupytext:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- uses: actions/setup-python@v5
31+
with:
32+
python-version: '3.12'
33+
34+
- name: Install jupytext
35+
run: pip install jupytext
36+
37+
- name: Generate .py mirrors from notebooks
38+
run: |
39+
# one-way export: each notebooks/**/X.ipynb -> notebooks/**/X.py (py:percent)
40+
find notebooks -name '*.ipynb' -not -path '*/.ipynb_checkpoints/*' \
41+
-exec jupytext --to py:percent {} +
42+
# drop any .py whose source notebook no longer exists
43+
find notebooks -name '*.py' | while read -r py; do
44+
[ -f "${py%.py}.ipynb" ] || { echo "removing orphan $py"; rm -f "$py"; }
45+
done
46+
47+
- name: Commit regenerated .py
48+
run: |
49+
git config user.name "github-actions[bot]"
50+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
51+
git add -A notebooks
52+
if git diff --cached --quiet; then
53+
echo "notebook .py mirrors already up to date"
54+
else
55+
# the commit only changes *.py, which doesn't match this workflow's
56+
# .ipynb path filter, so it won't retrigger; [skip ci] is belt-and-braces
57+
git commit -m "Auto-generate notebook .py mirrors from edited .ipynb [skip ci]"
58+
git push
59+
fi

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ We hope to have an `algorithm-name.md` file for each algorithm, eventually; it w
3838

3939
## Jupyter Notebooks
4040

41-
In this project we use Jupyter/IPython Notebooks to showcase the algorithms in the book. They serve as short tutorials on what the algorithms do, how they are implemented and how one can use them. To install Jupyter, you can follow the instructions [here](https://jupyter.org/install.html). These are some ways you can contribute to the notebooks:
41+
In this project we use Jupyter/IPython Notebooks to showcase the algorithms in the book. They serve as short tutorials on what the algorithms do, how they are implemented and how one can use them. To install Jupyter, you can follow the instructions [here](https://jupyter.org/install.html). Each notebook has an auto-generated `.py` mirror next to it (produced by the `notebooks-to-py.yml` GitHub Action via jupytext): the `.ipynb` is the source of truth — edit the notebook, never the generated `.py`. These are some ways you can contribute to the notebooks:
4242

4343
- Proofread the notebooks for grammar mistakes, typos, or general errors.
4444
- Move visualization and unrelated to the algorithm code from notebooks to `notebook_utils.py` (a file used to store code for the notebooks, like visualization and other miscellaneous stuff). Make sure the notebooks still work and have their outputs showing!

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The 4th edition of the book is out now in 2020, and thus we are updating the cod
2525
When complete, this project will have Python implementations for all the pseudocode algorithms in the book, as well as tests and examples of use. The code is organised into three top-level folders:
2626

2727
- **`aima/`** — the importable Python package: one module per major topic (e.g. `aima/search.py`) with the implementations of the pseudocode algorithms and their support functions/classes/data.
28-
- **`notebooks/`** — the Jupyter notebooks that explain and demonstrate the code (e.g. `notebooks/search.ipynb`), plus the per-chapter `notebooks/chapterNN/` demos. Each notebook starts with a small bootstrap cell so it runs correctly from the `notebooks/` folder (it `chdir`s to the repo root so `from aima import ...` and the `aima-data/`/`images/` paths resolve).
28+
- **`notebooks/`** — the Jupyter notebooks that explain and demonstrate the code (e.g. `notebooks/search.ipynb`), plus the per-chapter `notebooks/chapterNN/` demos. Each notebook starts with a `%run bootstrap.ipynb` cell that puts the repo root on `sys.path`, so `from aima import ...` works wherever the notebook is launched. A GitHub Action ([`notebooks-to-py.yml`](.github/workflows/notebooks-to-py.yml)) keeps a readable, diffable `.py` mirror of every notebook beside it (generated with [jupytext](https://jupytext.readthedocs.io)); the `.ipynb` is the source of truth, so edit the notebook, not the generated `.py`.
2929
- **`tests/`** — a lightweight test suite (e.g. `tests/test_search.py`), using `assert` statements, designed for use with [`py.test`](http://pytest.org/latest/) but also usable on their own.
3030

3131
# Python 3.9 and up

0 commit comments

Comments
 (0)