Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file is generated from `pyxray/src/pyxray/glossary.py`. Edit that and run `

## Index

[ASDL](#asdl) | [Argument Clinic](#argument-clinic) | [C3 linearization](#c3-linearization) | [EXTENDED_ARG](#extended_arg) | [GC pre header](#gc-pre-header) | [JIT](#jit) | [MRO](#mro) | [PEG parser](#peg-parser) | [PyVarObject](#pyvarobject) | [Pyodide](#pyodide) | [WebAssembly](#webassembly) | [abstract syntax tree](#abstract-syntax-tree) | [adaptive instruction](#adaptive-instruction) | [arena](#arena) | [assembler](#assembler) | [backtrace](#backtrace) | [basic block](#basic-block) | [binding](#binding) | [block](#block) | [blurb](#blurb) | [borrowed reference](#borrowed-reference) | [bytecode](#bytecode) | [cell](#cell) | [class cell](#class-cell) | [closure](#closure) | [code generation](#code-generation) | [code object](#code-object) | [coding cookie](#coding-cookie) | [cold block](#cold-block) | [computed goto](#computed-goto) | [configure](#configure) | [constant folding](#constant-folding) | [control flow graph](#control-flow-graph) | [cycle collector](#cycle-collector) | [deallocation](#deallocation) | [debug build](#debug-build) | [deoptimization](#deoptimization) | [devguide](#devguide) | [dispatch](#dispatch) | [environment changed](#environment-changed) | [eval loop](#eval-loop) | [evaluation order](#evaluation-order) | [exception table](#exception-table) | [f string](#f-string) | [finalizer](#finalizer) | [frame](#frame) | [free threaded build](#free-threaded-build) | [free variable](#free-variable) | [gdb](#gdb) | [generated file](#generated-file) | [generation](#generation) | [grammar](#grammar) | [header file](#header-file) | [heap type](#heap-type) | [immortal object](#immortal-object) | [indent and dedent](#indent-and-dedent) | [inline cache](#inline-cache) | [instance dictionary](#instance-dictionary) | [instruction](#instruction) | [interning](#interning) | [left recursion](#left-recursion) | [line table](#line-table) | [magic number](#magic-number) | [marshal](#marshal) | [metaclass](#metaclass) | [monitoring events](#monitoring-events) | [new reference](#new-reference) | [object](#object) | [object header](#object-header) | [obmalloc](#obmalloc) | [oparg](#oparg) | [opcode](#opcode) | [parser generator](#parser-generator) | [pdb](#pdb) | [pointer](#pointer) | [pool](#pool) | [product type](#product-type) | [profile guided optimization](#profile-guided-optimization) | [pseudo instruction](#pseudo-instruction) | [pyc file](#pyc-file) | [pyconfig](#pyconfig) | [reference count](#reference-count) | [reference cycle](#reference-cycle) | [reference leak](#reference-leak) | [regen](#regen) | [regrtest](#regrtest) | [replacement field](#replacement-field) | [resource](#resource) | [scope](#scope) | [segmentation fault](#segmentation-fault) | [short circuiting](#short-circuiting) | [slot](#slot) | [slot wrapper](#slot-wrapper) | [small integer cache](#small-integer-cache) | [soft keyword](#soft-keyword) | [specialization](#specialization) | [stack depth](#stack-depth) | [stack effect](#stack-effect) | [static type](#static-type) | [stolen reference](#stolen-reference) | [struct](#struct) | [sum type](#sum-type) | [symbol table](#symbol-table) | [symbol table pass](#symbol-table-pass) | [t string](#t-string) | [test case](#test-case) | [tier one](#tier-one) | [tier two](#tier-two) | [token](#token) | [tokenizer](#tokenizer) | [trace function](#trace-function) | [type object](#type-object) | [underflow](#underflow) | [value stack](#value-stack) | [weak reference](#weak-reference)
[ASDL](#asdl) | [Argument Clinic](#argument-clinic) | [C3 linearization](#c3-linearization) | [EXTENDED_ARG](#extended_arg) | [GC pre header](#gc-pre-header) | [JIT](#jit) | [MRO](#mro) | [PEG parser](#peg-parser) | [PyVarObject](#pyvarobject) | [Pyodide](#pyodide) | [WebAssembly](#webassembly) | [abstract syntax tree](#abstract-syntax-tree) | [adaptive instruction](#adaptive-instruction) | [arena](#arena) | [assembler](#assembler) | [backtrace](#backtrace) | [basic block](#basic-block) | [binding](#binding) | [block](#block) | [blurb](#blurb) | [borrowed reference](#borrowed-reference) | [bytecode](#bytecode) | [cell](#cell) | [class cell](#class-cell) | [closure](#closure) | [code generation](#code-generation) | [code object](#code-object) | [coding cookie](#coding-cookie) | [cold block](#cold-block) | [computed goto](#computed-goto) | [configure](#configure) | [constant folding](#constant-folding) | [control flow graph](#control-flow-graph) | [cycle collector](#cycle-collector) | [data descriptor](#data-descriptor) | [deallocation](#deallocation) | [debug build](#debug-build) | [deoptimization](#deoptimization) | [devguide](#devguide) | [dispatch](#dispatch) | [environment changed](#environment-changed) | [eval loop](#eval-loop) | [evaluation order](#evaluation-order) | [exception table](#exception-table) | [f string](#f-string) | [finalizer](#finalizer) | [frame](#frame) | [free threaded build](#free-threaded-build) | [free variable](#free-variable) | [gdb](#gdb) | [generated file](#generated-file) | [generation](#generation) | [grammar](#grammar) | [header file](#header-file) | [heap type](#heap-type) | [immortal object](#immortal-object) | [indent and dedent](#indent-and-dedent) | [inline cache](#inline-cache) | [instance dictionary](#instance-dictionary) | [instruction](#instruction) | [interning](#interning) | [left recursion](#left-recursion) | [line table](#line-table) | [magic number](#magic-number) | [marshal](#marshal) | [metaclass](#metaclass) | [monitoring events](#monitoring-events) | [new reference](#new-reference) | [object](#object) | [object header](#object-header) | [obmalloc](#obmalloc) | [oparg](#oparg) | [opcode](#opcode) | [parser generator](#parser-generator) | [pdb](#pdb) | [pointer](#pointer) | [pool](#pool) | [product type](#product-type) | [profile guided optimization](#profile-guided-optimization) | [pseudo instruction](#pseudo-instruction) | [pyc file](#pyc-file) | [pyconfig](#pyconfig) | [reference count](#reference-count) | [reference cycle](#reference-cycle) | [reference leak](#reference-leak) | [regen](#regen) | [regrtest](#regrtest) | [replacement field](#replacement-field) | [resource](#resource) | [scope](#scope) | [segmentation fault](#segmentation-fault) | [short circuiting](#short-circuiting) | [slot](#slot) | [slot wrapper](#slot-wrapper) | [small integer cache](#small-integer-cache) | [soft keyword](#soft-keyword) | [specialization](#specialization) | [stack depth](#stack-depth) | [stack effect](#stack-effect) | [static type](#static-type) | [stolen reference](#stolen-reference) | [struct](#struct) | [sum type](#sum-type) | [symbol table](#symbol-table) | [symbol table pass](#symbol-table-pass) | [t string](#t-string) | [test case](#test-case) | [tier one](#tier-one) | [tier two](#tier-two) | [token](#token) | [tokenizer](#tokenizer) | [trace function](#trace-function) | [type object](#type-object) | [underflow](#underflow) | [value stack](#value-stack) | [weak reference](#weak-reference)

## Reading the source

Expand Down Expand Up @@ -658,6 +658,14 @@ When a type written in C is made ready, `add_operators` walks the slot table and

Also written `wrapper_descriptor`. First met in O03. See also [slot](#slot), [type object](#type-object). In the source: [`Objects/typeobject.c:12456-12470@v3.15.0rc1#add_operators`](https://github.com/python/cpython/blob/v3.15.0rc1/Objects/typeobject.c#L12456-L12470).

### data descriptor

**An object on a type that has __set__ or __delete__ as well as __get__.**

The test is one line: `PyDescr_IsData` asks whether `tp_descr_set` is filled in. That one bit decides the whole precedence question, because attribute lookup calls a data descriptor before it ever looks in the instance dict and calls anything else after. `property` and the descriptors `__slots__` generates are data descriptors. A plain function is not, which is why you can shadow a method on one instance and cannot shadow a property.

Also written non data descriptor. First met in O05. See also [slot](#slot), [type object](#type-object). In the source: [`Objects/descrobject.c:1028-1032@v3.15.0rc1#PyDescr_IsData`](https://github.com/python/cpython/blob/v3.15.0rc1/Objects/descrobject.c#L1028-L1032).

### MRO

**The flat list of classes, in order, that a name is looked up in.**
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ A lesson can also end with a boss fight, which is a problem the text does not so
| O02 | [Following the type pointer](lessons/o02-following-the-type-pointer/o02.ipynb) | The biggest struct in the interpreter, read from Python: what basicsize and itemsize say about an instance and why list breaks the arithmetic, the one flag bit behind cannot set attribute of immutable type, the minus one in tp_dictoffset that is a sentinel rather than an offset, the thirty two bytes reserved in front of every instance of a class you wrote, and the five steps a class statement actually compiles to | M4 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tamnd/cpython-internals/blob/main/lessons/o02-following-the-type-pointer/o02.ipynb) |
| O03 | [Dunders and the slots behind them](lessons/o03-dunders-and-slots/o03.ipynb) | Where int.__add__ comes from when nobody wrote it, why setting __repr__ on an instance does nothing and setting it on a class works an hour later, how one __getitem__ makes a class iterable without an __iter__, why a subclass __radd__ beats a base class __add__, and the three steps between defining __eq__ and getting unhashable type | M4 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tamnd/cpython-internals/blob/main/lessons/o03-dunders-and-slots/o03.ipynb) |
| O04 | [The order things are found in](lessons/o04-the-order-things-are-found-in/o04.ipynb) | Why a class with two bases carries a flat list instead of searching, the twenty line merge that builds it and matches CPython exactly on classes out of the standard library, the two ways a class statement can fail before the class exists, why super goes somewhere the class it is written in has never heard of, and the two ways the list changes after the fact | M4 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tamnd/cpython-internals/blob/main/lessons/o04-the-order-things-are-found-in/o04.ipynb) |
| O05 | [What a dot does](lessons/o05-what-a-dot-does/o05.ipynb) | The four places x.name looks and the fixed order it looks in them, why a property cannot be shadowed by an instance attribute and a method can, why __getattr__ and __getattribute__ are not two versions of the same hook, why a class lookup starts at the metaclass and passes None where the instance would go, and the version tag that lets four thousand cache entries go stale without anybody clearing them | M4 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tamnd/cpython-internals/blob/main/lessons/o05-what-a-dot-does/o05.ipynb) |

More are landing in order. [lessons/README.md](lessons/README.md) explains how one is put together and how to run them locally.

Expand Down
70 changes: 70 additions & 0 deletions citations.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,11 @@
"first_line": "PyCodeObject *",
"lines": 4
},
"Objects/descrobject.c:1028-1032@v3.15.0rc1": {
"digest": "0bdba2c917b75359",
"first_line": "int",
"lines": 5
},
"Objects/listobject.c:103-134@v3.15.0rc1": {
"digest": "779a7953df231c11",
"first_line": "static int",
Expand Down Expand Up @@ -675,6 +680,26 @@
"first_line": "_PyObject_ComputedDictPointer(PyObject *obj)",
"lines": 23
},
"Objects/object.c:1887-1926@v3.15.0rc1": {
"digest": "e43f250aaa47532f",
"first_line": "_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,",
"lines": 40
},
"Objects/object.c:1964-1979@v3.15.0rc1": {
"digest": "c36e1e221a8cd787",
"first_line": "if (dict != NULL) {",
"lines": 16
},
"Objects/object.c:1981-2002@v3.15.0rc1": {
"digest": "f2770e63b5eb1bdf",
"first_line": "if (f != NULL) {",
"lines": 22
},
"Objects/object.c:1996-2002@v3.15.0rc1": {
"digest": "aa9e41e294ee44d7",
"first_line": "if (!suppress) {",
"lines": 7
},
"Objects/object.c:2773-2791@v3.15.0rc1": {
"digest": "84c3b29a29ccb125",
"first_line": "void",
Expand Down Expand Up @@ -705,6 +730,16 @@
"first_line": "#define SLOT1BINFULL(FUNCNAME, TESTFUNC, SLOTNAME, DUNDER, RDUNDER) \\",
"lines": 28
},
"Objects/typeobject.c:10990-11026@v3.15.0rc1": {
"digest": "c607830bbd258217",
"first_line": "PyObject *",
"lines": 37
},
"Objects/typeobject.c:11002-11009@v3.15.0rc1": {
"digest": "6081f882f79f8ec6",
"first_line": "if (getattr == NULL) {",
"lines": 8
},
"Objects/typeobject.c:11522-11531@v3.15.0rc1": {
"digest": "4ad77fa864aa2cae",
"first_line": "/*",
Expand All @@ -720,6 +755,11 @@
"first_line": "static pytype_slotdef slotdefs[] = {",
"lines": 7
},
"Objects/typeobject.c:1166-1195@v3.15.0rc1": {
"digest": "f65ccc798f6a3cbd",
"first_line": "type_modified_unlocked(PyTypeObject *type)",
"lines": 30
},
"Objects/typeobject.c:11740-11753@v3.15.0rc1": {
"digest": "77bf33636ec9ffc9",
"first_line": "MPSLOT(__len__, mp_length, slot_mp_length, wrap_lenfunc,",
Expand Down Expand Up @@ -750,6 +790,11 @@
"first_line": "static void",
"lines": 21
},
"Objects/typeobject.c:1389-1392@v3.15.0rc1": {
"digest": "7ed19048d6bc13f5",
"first_line": "#define MAX_VERSIONS_PER_CLASS 1000",
"lines": 4
},
"Objects/typeobject.c:1798-1810@v3.15.0rc1": {
"digest": "d8630aee772bf39e",
"first_line": "mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp)",
Expand Down Expand Up @@ -800,11 +845,36 @@
"first_line": "static PyObject *",
"lines": 20
},
"Objects/typeobject.c:44-58@v3.15.0rc1": {
"digest": "f718a74b28ee2783",
"first_line": "/* The cache can keep references to the names alive for longer than",
"lines": 15
},
"Objects/typeobject.c:6144-6180@v3.15.0rc1": {
"digest": "a16a8c9e914d75f2",
"first_line": "static int",
"lines": 37
},
"Objects/typeobject.c:6306-6345@v3.15.0rc1": {
"digest": "26beeb64230a5866",
"first_line": "_PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out)",
"lines": 40
},
"Objects/typeobject.c:6570-6600@v3.15.0rc1": {
"digest": "4e8db6ed47e730d6",
"first_line": "_PyStackRef",
"lines": 31
},
"Objects/typeobject.c:6600-6616@v3.15.0rc1": {
"digest": "7f7ce8c804fe44d1",
"first_line": "if (!PyStackRef_IsNull(meta_attribute_ref.ref)) {",
"lines": 17
},
"Objects/typeobject.c:6618-6650@v3.15.0rc1": {
"digest": "601da348b9cd58a9",
"first_line": "/* No data descriptor found on metatype. Look in tp_dict of this",
"lines": 33
},
"Objects/typeobject.c:7290-7295@v3.15.0rc1": {
"digest": "e3fd1c7921a386a3",
"first_line": "PyTypeObject PyType_Type = {",
Expand Down
16 changes: 15 additions & 1 deletion lessons/CLAIMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ header, what the allocator does with a freed block, the shape of the eval loop.
marked with the reason, and a lesson is allowed at most 3 of them. The cap is the point.
Without it the exception becomes the rule and this goes back to being a book.

272 claims across 32 lessons, 21 of them not observable from Python.
281 claims across 33 lessons, 22 of them not observable from Python.

## B01. Building CPython, and whether you need to

Expand Down Expand Up @@ -232,6 +232,20 @@ Without it the exception becomes the rule and this goes back to being a book.
| assigning to a class's __bases__ recomputes the MRO of that class and of every subclass, and instances that already exist pick up the new method at once | [`o04-22`](o04-the-order-things-are-found-in/o04.ipynb) |
| a metaclass that overrides mro can return an order the C3 rules would never produce, and attribute lookup uses that order without complaint | [`o04-24`](o04-the-order-things-are-found-in/o04.ipynb) |

## O05. What a dot does

| Claim | Proved by |
| --- | --- |
| with the same name present on the type and in the instance dict, a data descriptor on the type wins and a non data descriptor loses, and the instance dict entry is untouched either way | [`o05-07`](o05-what-a-dot-does/o05.ipynb) |
| __getattr__ runs only for names the normal lookup failed to find, while an overridden __getattribute__ runs for every name including __dict__ | [`o05-09`](o05-what-a-dot-does/o05.ipynb) |
| assigning __getattr__ to an instance leaves it in the instance dict and has no effect on attribute lookup | [`o05-11`](o05-what-a-dot-does/o05.ipynb) |
| a name defined on the metaclass is reachable through the class and not through an instance of it, and a descriptor found on a class is called with None where the instance would be | [`o05-13`](o05-what-a-dot-does/o05.ipynb) |
| the AttributeError for an instance and for a class have different wording, and the exception carries the name and the object as attributes | [`o05-15`](o05-what-a-dot-does/o05.ipynb) |
| the did you mean suggestion is not part of the exception message and only appears when a traceback is formatted, because it is computed from the obj attribute at that point | [`o05-17`](o05-what-a-dot-does/o05.ipynb) |
| the cache and the version tag are not visible from Python at all, since nothing exposes tp_version_tag, the hit rate, or the cache contents | not observable from Python: the effect is only measurable as a timing difference, and the specialised bytecode in the next section is the closest observable proxy |
| the same LOAD_ATTR instruction takes a different specialised form depending on whether the attribute lives in the instance values, in a slot, on a module, or is a method | [`o05-20`](o05-what-a-dot-does/o05.ipynb) |
| assigning a property over an attribute the specialised instruction was relying on makes the guard fail, and the instruction goes back to the general form and then settles on a different specialised one | [`o05-22`](o05-what-a-dot-does/o05.ipynb) |

## T01. One line, seven stages

| Claim | Proved by |
Expand Down
Loading
Loading