PR for tracking changes for immutability#63
Draft
mjp41 wants to merge 26 commits intoimmutable-basefrom
Draft
Conversation
* Changes made in sprint for PLDI deadline. Co-authored-by: xFrednet <xFrednet@gmail.com> * Addressing code review and CI failures. * Format * Windows build * More CI fixes * More CI fixes * Remove free-threaded build until we do that work. * Fix yaml lint (maybe) * Disable another free threaded build * Disable another free threaded build --------- Co-authored-by: xFrednet <xFrednet@gmail.com>
This commit moves the immutable C module into _immutable, and adds a new Python module that surfaces the immutable module to Python.
xFrednet
approved these changes
Jan 15, 2026
Includes various fixes to get more passing CI too Co-authored-by: Fridtjof Stoldt <xFrednet@gmail.com>
Fix Immutable Proxy Mode for Modules
* Missing clean up code * Fix limited API
Extend the freeze infrastructure with the ability to determine whether an object graph can be viewed as immutable without requiring an explicit freeze call. An object graph can be viewed as immutable if every reachable object is either already frozen or is "shallow immutable" (its own state cannot be mutated, but it may reference other objects). If the graph can be viewed as immutable, it is frozen to set up proper refcount management, and isfrozen() returns True. Key changes: - Add _PyImmutability_CanViewAsImmutable() C API that walks the object graph using an iterative DFS (avoiding stack overflow on deep graphs) and checks that every reachable object is shallow immutable or already frozen. If the check passes, the graph is frozen via the existing _PyImmutability_Freeze(). - Add _PyImmutability_RegisterShallowImmutable() C API for extension modules to register types whose instances are individually immutable. Built-in types (tuple, frozenset, bytes, str, int, float, complex, bool, NoneType, etc.) are registered at init time. - Immutable type objects (Py_TPFLAGS_IMMUTABLETYPE) are recognized as shallow immutable without explicit registration. - Change type_reachable() to visit tp_dict directly rather than via lookup_tp_dict(). For static builtin types, tp_dict is NULL on the struct (stored in per-interpreter state), so the walk skips these internal dicts — avoiding false failures on type internals. - Integrate the check into isfrozen(): it now returns True for objects that are explicitly frozen OR can be viewed as immutable (freezing as a side effect in the latter case). - Add ShallowImmutable test type to _test_reachable C module to exercise RegisterShallowImmutable from C. - Add 24 tests covering: tuples/frozensets of various types, mutable containers (correctly rejected), already-frozen objects, deeply nested structures (10,000 deep — verifies no stack overflow), and C-registered shallow immutable types.
Add set_freezable(obj, status) to control whether individual objects
can be frozen. The status is one of four values:
FREEZABLE_YES - always freezable
FREEZABLE_NO - never freezable
FREEZABLE_EXPLICIT - freezable only when freeze() is called directly
on the object, not when reached as a child
FREEZABLE_PROXY - reserved for future module proxy use
Storage uses a two-tier strategy: first tries setting a __freezable__
attribute on the object (works for any object with a __dict__), falling
back to bits in the header.
During freeze, check_freezable() queries the status by checking the
__freezable__ attribute first, then the weakref dictionary. For
EXPLICIT, the freeze root object is tracked in FreezeState so that
direct vs child freezing can be distinguished.
The old standalone __freezable__ = False check in traverse_freeze is
removed; all __freezable__ handling is now unified through
check_freezable() and _PyImmutability_GetFreezable().
Changes:
- Include/internal/pycore_immutability.h: add _Py_freezable_status
enum, freezable_objects dict, and destroy_objects_cb to state
- Include/cpython/immutability.h: declare SetFreezable/GetFreezable
- Python/immutability.c: implement SetFreezable (attr-first, bits in header
fallback), GetFreezable (attr-first, weakref fallback),
_destroy_dict callback, FreezeState.root, updated check_freezable
- Modules/_immutablemodule.c: expose set_freezable() and constants
- Lib/immutable.py: export set_freezable and FREEZABLE_* constants
- Python/pystate.c: cleanup new state fields in interpreter_clear()
- Lib/test/test_freeze/test_set_freezable.py: 18 tests covering all
status values, storage strategies, edge cases, and constants
Co-authored-by: Fridtjof Stoldt <xFrednet@gmail.com>
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.
This is a Meta-PR that tracks the overall changes from the CPython 3.15 alpha to enable immutability.