Skip to content

Declare every lesson cell whose output depends on the Python version - #73

Merged
tamnd merged 2 commits into
mainfrom
lesson-version-notes
Aug 29, 2026
Merged

Declare every lesson cell whose output depends on the Python version#73
tamnd merged 2 commits into
mainfrom
lesson-version-notes

Conversation

@tamnd

@tamnd tamnd commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Second half of #2. #72 built nbversion and deliberately left the lessons unannotated, because annotating them touches all twelve and would have buried the tool in the diff. This is that part, plus the CI job that makes the check gating.

uv run nbversion compare build/versions/3.15 build/versions/3.14 now reports 64 declared cells, no undeclared, no stale and no missing.

The CI job

The notebooks matrix already runs every cell on 3.15 and on 3.14. Each leg now also records what its interpreter printed and uploads it as an artifact, and a new versions job downloads both and compares them.

Recording is a separate step from nbcheck run on purpose. nbcheck run stops at the first cell that raises, which is what you want when a lesson is broken. nbversion record keeps going, so one broken lesson cannot hide every version difference in the lessons after it.

The four differences

Four things account for most of the 64, so their wording lives in nbbuild.notes and every lesson that hits one says the same sentence. A reader who meets a note in T05 and again in T10 gets the same words both times, and when 3.16 changes one of them there is one place to fix.

On 3.15 the implicit return None at the end of a module is a LOAD_COMMON_CONSTANT and None is not in co_consts at all. On 3.14 it is an ordinary LOAD_CONST and None is in the table, so you get one more constant and two fewer bytes of bytecode.

RESUME and GET_ITER carry an inline cache entry on 3.15 and do not on 3.14, which moves every offset in every listing by two to four.

The shared range of small integers stops at 1024 on 3.15 and 256 on 3.14, which is why 257 is 257 changed its answer.

And several cells count files in the reader's own standard library, which is a build choice rather than a version. A framework install, a source build and a Colab image all disagree, and they are all right.

Quiet notes and visible ones

A note is quiet when a paragraph nearby already explains the difference, and visible when the cell is the only one that hits it. T05, T06, T07 and Z02 gained that paragraph. Thirteen cells carry a visible note. Twelve of the quiet ones are the pyxray.show() banner, which prints the caveat itself, and repeating it under all twelve would teach people to skip the notes.

Three judgement calls

T08's central observation is the small integer cache, which is one of the differing ones. The policy from #72 says a note is not enough there, so the lesson measures the range rather than asserting a number, and both answers are correct on the interpreter that produced them.

T07's stack chain prints names and line numbers from asyncio and Jupyter, which belong to whatever is running the notebook rather than to anything the lesson did. The note says to read the bottom of the list, which is the reader's own three functions.

sys.monitoring allows 17 local events on 3.15 and 12 on 3.14, because the exception events did not qualify before. T07 already discussed that in prose before this check existed, and the cell asks the reader's own build rather than quoting either number.

The two kinds of note

The first run of the versions job found two stale notes that are not stale, which is the most useful thing it could have done.

On a runner both interpreters come from the same builder, so the cell that prints your build's configure flags prints the same thing twice and the check concluded the note had stopped being true. On a laptop with a framework Python next to a uv-managed one, it differs, and the note is right. Neither machine is wrong, and two recordings cannot tell you which.

So there are two keys now. differs= is a claim about the language: this prints one thing on 3.14 and another on 3.15. Two recordings can check it, and it fails when it stops being true. varies= is a claim about the reader's machine: which flags it was configured with, how many files are in its standard library, how deep the C stack goes before it runs out. It reads identically to a reader and the comparison reports it without judging it, because the judgement would depend on which two machines happened to make the recordings.

Ten cells moved to varies=, and nbversion compare now reports 53 declared and 11 noted with nothing failing. Lesson.code refuses a cell that is both, since guessing which one the author meant would put the wrong thing in the metadata.

Checking

just check is green, and just versions records both interpreters from scratch and compares them.

Closes the annotation, CI check and visible note boxes on #2. The remaining box there is tracking Pyodide's 3.15 release.

tamnd added 2 commits August 29, 2026 09:37
Second half of #2. #72 built the tool and left the lessons unannotated,
because annotating them touches all twelve.

`nbversion compare` now reports 64 declared cells and nothing else, so the
`versions` job in CI can gate on it. The job hangs off the notebooks matrix:
each leg records what its interpreter printed and uploads it, and one job
downloads both and compares.

Four differences account for most of it, so their wording lives in
nbbuild.notes and every lesson that hits one says the same sentence. On 3.15
the implicit `return None` is a LOAD_COMMON_CONSTANT and None is not in
co_consts. RESUME and GET_ITER carry an inline cache in 3.15 and not in 3.14,
which moves every offset by two to four. The shared range of small integers
stops at 1024 rather than 256. And several cells count files in the reader's
own installation, which is a build choice rather than a version.

Notes are quiet where a paragraph nearby already explains the difference, and
visible where the cell is the only one that hits it. T05, T06, T07 and Z02
gained that paragraph. Thirteen cells carry a visible note. Twelve of the
quiet ones are the banner, which prints the caveat itself.

The three interesting judgement calls. T08's central observation is the small
integer cache, so the lesson measures it rather than asserting a number, and
both answers are correct on the interpreter that gives them. T07's stack chain
prints line numbers from asyncio and Jupyter, which belong to whatever is
running the notebook, so the note says to read the bottom of the list. And
`sys.monitoring` has 17 local events on 3.15 and 12 on 3.14, which the lesson
already discussed in prose before this check existed.
CI found two stale notes that are not stale. On a runner both interpreters
come from the same builder, so a cell that prints the build's configure flags
prints the same thing twice and the check concluded the note had stopped
being true. On a laptop with a framework Python and a uv-managed one it
differs, and the note is right there. Neither machine is wrong, and comparing
two recordings cannot tell you which.

So there are two keys now. `differs=` is a claim about the language: this
prints one thing on 3.14 and another on 3.15. Two recordings can check it, and
it fails when it stops being true. `varies=` is a claim about the reader's
machine: the flags it was built with, how many files are in its standard
library, how deep the C stack goes. It reads identically to a reader and the
comparison reports it without judging it, because the judgement would depend
on which two machines happened to make the recordings.

Ten cells moved to `varies=`. Locally the comparison now says 53 declared and
11 noted with nothing failing, and on a runner where the two builds agree the
same eleven still pass.

`Lesson.code` refuses a cell that is both, since guessing which one the author
meant would put the wrong thing in the metadata.
@tamnd
tamnd merged commit ce91c12 into main Aug 29, 2026
9 checks passed
@tamnd
tamnd deleted the lesson-version-notes branch August 29, 2026 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant