Declare every lesson cell whose output depends on the Python version - #73
Merged
Conversation
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.
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second half of #2. #72 built
nbversionand 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.14now reports 64 declared cells, no undeclared, no stale and no missing.The CI job
The
notebooksmatrix 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 newversionsjob downloads both and compares them.Recording is a separate step from
nbcheck runon purpose.nbcheck runstops at the first cell that raises, which is what you want when a lesson is broken.nbversion recordkeeps 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.notesand 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 Noneat the end of a module is aLOAD_COMMON_CONSTANTandNoneis not inco_constsat all. On 3.14 it is an ordinaryLOAD_CONSTandNoneis in the table, so you get one more constant and two fewer bytes of bytecode.RESUMEandGET_ITERcarry 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 257changed 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.monitoringallows 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
versionsjob 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=, andnbversion comparenow reports 53 declared and 11 noted with nothing failing.Lesson.coderefuses a cell that is both, since guessing which one the author meant would put the wrong thing in the metadata.Checking
just checkis green, andjust versionsrecords 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.