Issue #16602: When a weakref's target was part of a long deallocation chain, the object could remain reachable through its weakref even though its refcount had dropped to zero.
Thanks to Eugene Toder for diagnosing and reporting the issue.
diff --git a/Include/weakrefobject.h b/Include/weakrefobject.h
index 7258387..537e7eb 100644
--- a/Include/weakrefobject.h
+++ b/Include/weakrefobject.h
@@ -70,7 +70,17 @@
PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self);
#endif
-#define PyWeakref_GET_OBJECT(ref) (((PyWeakReference *)(ref))->wr_object)
+/* Explanation for the Py_REFCNT() check: when a weakref's target is part
+ of a long chain of deallocations which triggers the trashcan mechanism,
+ clearing the weakrefs can be delayed long after the target's refcount
+ has dropped to zero. In the meantime, code accessing the weakref will
+ be able to "see" the target object even though it is supposed to be
+ unreachable. See issue #16602. */
+
+#define PyWeakref_GET_OBJECT(ref) \
+ (Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \
+ ? ((PyWeakReference *)(ref))->wr_object \
+ : Py_None)
#ifdef __cplusplus