Skip to content

spec 1724: Builtins/types panel re-audit to ground-truth green#95

Merged
tamnd merged 4 commits into
mainfrom
feat/v0.13.6-spec-1724-builtins-types-reaudit
Jun 19, 2026
Merged

spec 1724: Builtins/types panel re-audit to ground-truth green#95
tamnd merged 4 commits into
mainfrom
feat/v0.13.6-spec-1724-builtins-types-reaudit

Conversation

@tamnd

@tamnd tamnd commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Re-ran the whole Builtins/types panel (spec 1724) under the 1726 bridge to get
ground truth, then fixed everything that turned out to be a real gopy bug. The
panel is now 28 of 29 files fully green; the one file with residuals (test_dict)
is down to two documented implementation-detail divergences.

What was actually broken

  • int_max_str_digits leaked across subinterpreters. The limit lived in a
    package global, so a child interpreter from _testcapi.run_in_subinterp could
    change it and leave the parent clobbered. CPython keeps it on the interpreter
    state. Added a snapshot/restore hook driven from PushSubinterp/PopSubinterp and
    registered it from module/sys. Fixes
    test_int.test_int_max_str_digits_is_per_interpreter.

  • Type creation ordered the synthesized dict/weakref descriptors ahead
    of the class body.
    A class that declares its own __dict__ lost to the
    generated one. CPython installs those getsets after copying the namespace in
    and uses SetDefaultRef semantics so a user slot wins. Deferred the install
    behind a name guard. Fixes test_builtin.test_namespace_order.

  • The default breakpoint hook left a stale thread exception. When
    PYTHONBREAKPOINT names an unimportable target the hook swallows the
    ModuleNotFoundError in favour of a RuntimeWarning, but did not clear the thread
    exception the way sys_breakpointhook calls PyErr_Clear, so a later handler saw
    the stale error. Fixes test_builtin.test_envar_unimportable.

  • Two test-only C helpers were missing. getbuffer_with_null_view
    (test_bytes) and dict_getitem_knownhash (test_dict CAPI test). Ported both,
    plus a KnownHash lookup that raises KeyError on a miss.

Left as documented residuals

test_dict keeps two failures, both @cpython_only-style implementation details:

  • test_splittable_popitem inspects the PEP 412 split-table dict layout through
    sys.getsizeof. gopy uses a single combined representation, so there is no
    split-to-combined growth to observe.
  • test_oob_indexing_dictiter_iternextitem relies on the items iterator
    recycling one result tuple and decreffing the previous value to zero mid-loop,
    firing __del__ synchronously. gopy's IterNext returns a borrowed reference
    (spec 1727) and allocates a fresh tuple per advance; the in-place reuse that
    would match CPython previously broke sorted(d.items()).

Both would mean reversing a deliberate gopy design choice for no Python-visible
behaviour, so they stay as residuals with citations in the spec rather than a
shim. The full reasoning is in website/docs/specs/1700/1724.

Ground truth for the whole panel (run from test/cpython/): every file OK except
the two test_dict residuals above.

tamnd added 4 commits June 19, 2026 11:02
The string-to-int conversion limit lives in a package global, so a child
interpreter spun up by _testcapi.run_in_subinterp could change it and leave
the parent's value clobbered. CPython parks the limit on the interpreter
state (interp->long_state.max_str_digits), so each interpreter has its own.

Add a snapshot/restore hook: PushSubinterp captures the parent value and
PopSubinterp restores it, registered from module/sys. Fixes
test_int.test_int_max_str_digits_is_per_interpreter.
type creation was adding the managed-dict and weakref getset descriptors
before copying the class body into tp_dict, so they sorted ahead of the
namespace keys and a class that declared its own __dict__ lost to the
synthesized one. CPython runs type_new_descriptors after type_new_set_attrs
and uses PyDict_SetDefaultRef, so a user-provided slot wins. Defer the
install behind an nsHasName guard to match.

Also clear the thread exception in the default breakpoint hook before
warning: when PYTHONBREAKPOINT names an unimportable target the hook
swallows the ModuleNotFoundError in favour of a RuntimeWarning, the same as
sys_breakpointhook calling PyErr_Clear, so a later handler does not observe
the stale error. Fixes test_builtin test_namespace_order and
test_envar_unimportable.
The 1726 bridge runs the cpython_only tests that exercise these test-only
C helpers. test_bytes.test_obsolete_write_lock asks _testcapi to call
PyObject_GetBuffer with a NULL Py_buffer* and expects BufferError;
test_dict's CAPI test calls _testinternalcapi.dict_getitem_knownhash. Port
both, plus a GetItemKnownHashOrKeyError that raises KeyError on a miss the
way _PyDict_GetItem_KnownHash does. Also clear the thread exception when
bytes(int) rejects a value so the swallowed error does not linger.
Re-ran the whole Builtins/types panel under the 1726 bridge for ground
truth. The earlier residuals for test_int (subinterpreter leak), test_bytes
(null-view helper) and test_builtin (breakpoint clear + namespace order) are
fixed; only test_dict's two impl-detail divergences remain. Update the spec
checklist, the residuals list, and the manifest counts to match.
@tamnd

tamnd commented Jun 19, 2026

Copy link
Copy Markdown
Owner Author

CI is green across the matrix (macOS, Linux, Windows) plus vet, lint and cfg-phase-parity.

For the record, the full Builtins/types panel run from test/cpython/ this build:

  • 28 of 29 files OK (test_buffer stays out of scope, C buffer-struct API)
  • test_dict: 118/120, the two remaining are the documented split-table and iterator-del-timing residuals

The two residuals are impl-detail only and chasing either would reverse a deliberate design choice (no PEP 412 split tables; borrow-model iterator from spec 1727), so I left them documented with citations rather than shimmed.

@tamnd tamnd merged commit 9cfad47 into main Jun 19, 2026
6 checks passed
@tamnd tamnd deleted the feat/v0.13.6-spec-1724-builtins-types-reaudit branch June 19, 2026 09:41
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