@@ -79,6 +79,21 @@ int init_state(struct _Py_immutability_state *state)
7979 return 0 ;
8080}
8181
82+ // This is separate to the previous init as it depends on the traceback
83+ // module being available, and can cause a circular import if it is
84+ // called during register freezable.
85+ static
86+ void init_traceback_state (struct _Py_immutability_state * state )
87+ {
88+ #ifdef Py_DEBUG
89+ PyObject * traceback_module = PyImport_ImportModule ("traceback" );
90+ if (traceback_module != NULL ) {
91+ state -> traceback_func = PyObject_GetAttrString (traceback_module , "format_stack" );
92+ Py_DECREF (traceback_module );
93+ }
94+ #endif
95+ }
96+
8297static struct _Py_immutability_state * get_immutable_state (void )
8398{
8499 PyInterpreterState * interp = PyInterpreterState_Get ();
@@ -509,6 +524,25 @@ int _PyImmutability_Freeze(PyObject* obj)
509524 return -1 ;
510525 }
511526
527+ PyObject * freeze_location = NULL ;
528+ #ifdef Py_DEBUG
529+ // In debug mode, we can set a freeze location for debugging purposes.
530+ // Get a traceback object to use as the freeze location.
531+ if (state -> traceback_func == NULL ) {
532+ init_traceback_state (state );
533+ }
534+
535+ if (state -> traceback_func != NULL ) {
536+ PyObject * stack = PyObject_CallFunctionObjArgs (state -> traceback_func , NULL );
537+ if (stack != NULL ) {
538+ // Add the type name to the top of the stack, can be useful.
539+ PyObject * typename = PyObject_GetAttrString (_PyObject_CAST (Py_TYPE (obj )), "__name__" );
540+ push (stack , typename );
541+ freeze_location = stack ;
542+ }
543+ }
544+ #endif
545+
512546 if (_Py_IsImmutable (obj )){
513547 return result ;
514548 }
@@ -564,6 +598,18 @@ int _PyImmutability_Freeze(PyObject* obj)
564598 if (_Py_IsImmutable (item )){
565599 continue ;
566600 }
601+ #ifdef Py_DEBUG
602+ if (freeze_location != NULL ) {
603+ // TODO(Immutable): Some objects don't have attributes that can be set.
604+ // As this is a Debug only feature, we could potentially increase the object
605+ // size to allow this to be stored directly on the object.
606+ if (PyObject_SetAttrString (item , "__freeze_location__" , freeze_location ) < 0 ) {
607+ // Ignore failure to set _freeze_location
608+ PyErr_Clear ();
609+ // We still want to freeze the object, so we continue
610+ }
611+ }
612+ #endif
567613
568614 _Py_SetImmutable (item );
569615
0 commit comments