R07. The stable ABI - #187
Merged
Merged
Conversation
The two gates an extension has to get past before it is a module, and both of them driven from Python rather than described. The first gate is the file name. It is read before anything is opened, and the tag has to be one of the handful in the C array at Python/dynload_shlib.c. The lesson drives it by writing empty files into temporary directories and asking PathFinder what it sees, which makes the whole rule fall out: a free threaded build takes abi3t and refuses abi3, an ordinary 3.15 build takes both and prefers abi3, and 3.14 has never heard of abi3t. Putting all of them in one directory shows the order comes from the array and that a compiled file shadows a Python one. The second gate is PyABIInfo, twelve bytes and new in 3.15. Because PyABIInfo_Check is a plain exported function the struct can be built in ctypes and handed to the real thing, so the four refusals print with CPython's own wording rather than a paraphrase. The stable ABI itself is dated by reading the preprocessor gates around the declarations: 173 functions added since 3.2, with the busiest releases being the recent ones. Two Tier 1 recordings run both gates on a release build and on a build made with --disable-gil, where six tags become four and four refusals become six. That is the argument for abi3t in two files. Six diagrams, three glossary terms, and ten of the notebook's cells run under Pyodide, with only the PyABIInfo cells and the two header reading cells sitting behind a guard.
The two R07 experiments printed file name tags with the build's machine in them, which is aarch64-linux-gnu when they are recorded here and x86_64-linux-gnu when CI verifies them, so tier1 verify failed on every line that carried a tag. The machine says nothing about either gate, so the program now replaces it with the word PLATFORM before printing, and the docstring says so. Both experiments re-recorded. The three measured lines are unchanged.
The lesson embeds both recordings, so taking the machine out of them changed the notebook too.
5 tasks
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.
R06 sorted out which C functions an extension is allowed to call. R07 picks up where that leaves off, when the thing you compiled turns up on somebody else's machine as a file, and asks what stands between that file and a working module.
The answer is two gates, and the point of the lesson is that both of them can be driven from Python rather than described.
The first gate is the file name. It is read before anything is opened, so it costs one stat call, and the tag has to be one of the six entries in the C array at
Python/dynload_shlib.c:39-60. The lesson drives it by writing empty files into temporary directories and askingPathFinder.find_specwhat it sees. Nothing loads, because the files are empty, but the rule falls out completely: a free threaded build takesabi3tand refusesabi3, an ordinary 3.15 build takes both and prefersabi3, and 3.14 has never heard ofabi3t. Putting all five candidates plus ademo.pyin one directory shows that the order comes straight from that array, and that a compiled file quietly shadows a Python file of the same name.There is a small piece of history in here too. The
3inabi3is not a Python version. It isPYTHON_ABI_VERSION, whichInclude/patchlevel.h:53-62says has not moved since 2010, sitting next toPYTHON_API_VERSION 1013, which has not moved since 2006.The second gate is
PyABIInfo, twelve bytes and new in 3.15. This is the part I was happiest to find.PyABIInfo_Checkis a plainPyAPI_FUNCin the public headers, and the struct is five integers, soctypescan build one and call the real check. Eight hypothetical extensions go in, four refusals come back, and they print with CPython's own wording rather than a paraphrase, becausectypes.pythonapinotices theImportErrorand raises it.The lesson is also honest about how weak that gate is. It is opt in, an extension that does not carry the struct is not checked at all, and the extension's own initialisation has already run by the time the check happens.
Doc/c-api/stable.rst:107-113says as much in the documentation.The stable ABI as a list that grew. The last two cells walk every public header as a preprocessor stack and date each declaration by the version gate around it. 173 functions added since 3.2, with the busiest releases being 3.13, 3.14 and 3.15, and the 528 that were already there in 3.2 not on the chart at all because there is no version to gate on. The same scan pointed at four names gives the file, the line and the version for each, which includes the mildly funny result that
PyABIInfo_Checkis itself gated on 3.15.Two Tier 1 recordings run both gates in containers, one on a release build and one on a build made with
--disable-gil. Six file name tags become four and four refusals become six. Both gates tightened, and they tightened in the same direction, which is the whole argument forabi3tin two files.Browser story. Ten cells, all ten run clean under Pyodide, and six of them do real work there rather than printing a guard message. Only the two
PyABIInfocells (3.15 only) and the two header reading cells (Pyodide ships no C headers, see #186) are guarded. That is a good deal better than R06, where five cells sat behind the header flag.Six diagrams, all under 1100 px. Three new glossary terms: extension module, SOABI, PyABIInfo. Word counts are hook 106, tour 1493, lesson 2080, all inside the caps.
Every citation is checked against the pin, the notebook executes clean on 3.15.0rc1 and 3.14, and the
differs=notes were written from actual 3.14 runs rather than guessed.