From be00d820862fd57ae1acef2dddd391f5b19b4559 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 16 Mar 2026 22:38:08 +0100 Subject: [PATCH 1/3] Add artifact markers to parts of the implementation --- Include/refcount.h | 3 +++ Lib/immutable.py | 2 ++ Lib/test/test_freeze/README.md | 8 ++++++++ Modules/_immutablemodule.c | 3 ++- Objects/funcobject.c | 1 + Objects/moduleobject.c | 1 + Objects/weakrefobject.c | 1 + Python/crossinterp.c | 1 + Python/immutability.c | 12 ++++++++++++ 9 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_freeze/README.md diff --git a/Include/refcount.h b/Include/refcount.h index 4279a8f690b317..204e06cfacd7b0 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -142,6 +142,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsImmutable(PyObject *op) } #define _Py_IsImmutable(op) _Py_IsImmutable(_PyObject_CAST(op)) +// Artifact[Implementation]: The definition of the `Py_CHECKWRITE` macro // Check whether an object is writeable. // This check will always succeed during runtime finalization. #define Py_CHECKWRITE(op) ((op) && (!_Py_IsImmutable(op) || _PyImmModule_Check(op) || Py_IsFinalizing())) @@ -388,6 +389,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) return; } #ifndef Py_LIMITED_API + // Artifact[Implementation]: The atomic RC branch for immutable objects in Py_INCREF if (_Py_IsImmutable(op)) { // Object is immutable. // Slight chance of overflow, and an issue here, so check, and @@ -559,6 +561,7 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op) return; } #ifndef Py_LIMITED_API + // Artifact[Implementation]: The atomic RC branch for immutable objects in Py_DECREF if (_Py_IsImmutable(op)) { if (_Py_DecRef_Immutable(op)) { diff --git a/Lib/immutable.py b/Lib/immutable.py index fc87f6310b9c68..e1c00152f94bbd 100644 --- a/Lib/immutable.py +++ b/Lib/immutable.py @@ -27,6 +27,8 @@ # aliases it to `is_frozen` isfrozen = is_frozen + +# Artifact[Benchmarking]: The implementation of immutability related decorators def freezable(cls): """Class decorator: mark a class as always freezable.""" set_freezable(cls, FREEZABLE_YES) diff --git a/Lib/test/test_freeze/README.md b/Lib/test/test_freeze/README.md new file mode 100644 index 00000000000000..cd4fbc4c07ba7b --- /dev/null +++ b/Lib/test/test_freeze/README.md @@ -0,0 +1,8 @@ +# Tests for Immutability + + + +This folder contains tests for individual parts of the immutability +implementation. diff --git a/Modules/_immutablemodule.c b/Modules/_immutablemodule.c index 553ece6d75f852..94ea460c340526 100644 --- a/Modules/_immutablemodule.c +++ b/Modules/_immutablemodule.c @@ -189,6 +189,7 @@ _immutable_unset_freezable(PyObject *module, PyObject *obj) Py_RETURN_NONE; } +// Artifact[Implementation]: The implementation of the `InterpreterLocal` type /* * InterpreterLocal type * @@ -360,6 +361,7 @@ static PyType_Spec interpreterlocal_spec = { .slots = interpreterlocal_slots, }; +// Artifact[Implementation]: The implementation of the `SharedField` type /* * SharedField type @@ -373,7 +375,6 @@ static PyType_Spec interpreterlocal_spec = { * can be accessed concurrently from different sub-interpreters (each * with its own GIL). */ - typedef struct { PyObject_HEAD PyObject *value; // Always frozen; guarded by lock diff --git a/Objects/funcobject.c b/Objects/funcobject.c index e92aee26078221..33b18cb34e4b4b 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1205,6 +1205,7 @@ func_descr_get(PyObject *func, PyObject *obj, PyObject *type) return PyMethod_New(func, obj); } +// Artifact[Implementation]: The pre-freeze hook of function objects /** * Special function for replacing globals and builtins with a copy of just what they use. * diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index c8e1b6394baf5d..fb94c6a648f48b 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1554,6 +1554,7 @@ module_make_immutable_proxy(PyObject *self) { return 0; } +// Artifact[Implementation]: The pre-freeze hook of module objects static int module_prefreeze(PyObject *self) { // TODO(immutable): Check if the module defines a custom pre-freeze hook: diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 66792e1c71743c..0abf281112e766 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -33,6 +33,7 @@ */ #else +// Artifact[Implementation]: Explanation how weak references work for immutable objects /* * Thread-safety for immutable objects * =================================== diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 2b27e0b8424157..ac3e7f538abeb1 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -488,6 +488,7 @@ _get_xidata(PyThreadState *tstate, return -1; } + // Artifact[Implementation]: The branch that allows direct sharing for immutable object across sub-interpreters if (_Py_IsImmutable(obj)) { _Py_IncRef(obj); xidata->obj = obj; diff --git a/Python/immutability.c b/Python/immutability.c index ee22fa84e4fe29..6add61cdcc7190 100644 --- a/Python/immutability.c +++ b/Python/immutability.c @@ -245,6 +245,7 @@ static PyObject* pop(PyObject* s){ +// Artifact[Implementation]: Explanation how a stack is used to implement the DFS based SCC algorithm /** * The DFS walk for SCC calculations needs to perform actions on both * the pre-order and post-order visits to an object. To achieve this @@ -290,6 +291,7 @@ static bool is_c_wrapper(PyObject* obj){ return PyCFunction_Check(obj) || Py_IS_TYPE(obj, &_PyMethodWrapper_Type) || Py_IS_TYPE(obj, &PyWrapperDescr_Type); } +// Artifact[Implementation]: The state used to track a single freeze call and construct SCCs /** * Used to track the state of an in progress freeze operation. * @@ -2015,6 +2017,7 @@ static void make_weakrefs_safe(struct FreezeState* freeze_state) /* This undoes a freeze belonging to the given state */ static void undo_freeze(struct FreezeState* state) { + // Artifact[Implementation]: The function that rolls back immutability on failure debug("Unfreezing all frozen objects belonging to %p\n", state); // Clear dfs stack @@ -2237,6 +2240,15 @@ late_init(struct _Py_immutability_state *state) static int freeze_impl(PyObject *const *objs, Py_ssize_t nobjs) { + // Artifact[Implementation]: The entry point to the `freeze()` function in C + // + // This is the central function to the freeze algorithm that handles + // DFS traversal and calls into other functions to: + // - Checking freezability + // - Checking and calling the pre-freeze hook + // - Construct SCCs + // - Handle failures + // - Remove objects from the GC list struct _Py_immutability_state* imm_state = NULL; int result = 0; TRACE_MERMAID_START(); From c9132fd060ccb1ae00a5379e36c7a530414dbc51 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 17 Mar 2026 00:39:17 +0100 Subject: [PATCH 2/3] Add missing tp_reachable --- Objects/funcobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 33b18cb34e4b4b..6106a3dced0209 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1738,6 +1738,7 @@ PyTypeObject PyClassMethod_Type = { PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ PyObject_GC_Del, /* tp_free */ + .tp_reachable = _PyObject_ReachableVisitType, }; PyObject * From 2c906885f84fa80a4227a26eb42341cf161d774b Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 17 Mar 2026 00:54:05 +0100 Subject: [PATCH 3/3] Allow the Proxy freezability --- Python/immutability.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/immutability.c b/Python/immutability.c index 6add61cdcc7190..0c4a83716b3d3f 100644 --- a/Python/immutability.c +++ b/Python/immutability.c @@ -1470,8 +1470,8 @@ static int check_freezable(struct _Py_immutability_state *state, PyObject* obj, } goto error; case _Py_FREEZABLE_PROXY: - // Reserved for future use — fall through to existing checks. - break; + assert(PyModule_Check(obj) || obj == _PyObject_CAST(&PyModule_Type)); + return 0; } }