From f3e9600db6086e834c0503cdaf30b5ba7ed53ceb Mon Sep 17 00:00:00 2001 From: Anonymous Coward Date: Sun, 26 Jul 2026 16:13:18 +0200 Subject: [PATCH] Replace PyWeakref_GetObject with PyWeakref_GetRef PyWeakref_GetObject was removed in Python 3.15. Replace all four call sites with PyWeakref_GetRef, which returns a strong reference instead of a borrowed one, and adjust reference counting accordingly. In Tf_PyIdHandle::Ptr(), return Py_None when the referent is dead to match the original behaviour (previously returned NULL erroneously). In Tf_PyWeakObject::GetObject(), explicitly return a Python None object on failure, matching the original semantics exactly. Fixes: https://github.com/PixarAnimationStudios/OpenUSD/issues/3966 Assisted-by: Claude Opus 4.6 Reviewed-by: OpenUSD Maintainers --- pxr/base/tf/pyFunction.h | 13 +++++++++---- pxr/base/tf/pyIdentity.cpp | 10 +++++++++- pxr/base/tf/pyWeakObject.cpp | 12 +++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/pxr/base/tf/pyFunction.h b/pxr/base/tf/pyFunction.h index 85af943b2..f77b5b71b 100644 --- a/pxr/base/tf/pyFunction.h +++ b/pxr/base/tf/pyFunction.h @@ -49,11 +49,13 @@ struct TfPyFunctionFromPython using namespace pxr_boost::python; // Attempt to get the referenced callable object. TfPyLock lock; - object callable(handle<>(borrowed(PyWeakref_GetObject(weak.ptr())))); - if (TfPyIsNone(callable)) { + PyObject *rawCallable = NULL; + if (PyWeakref_GetRef(weak.ptr(), &rawCallable) <= 0) { TF_WARN("Tried to call an expired python callback"); return Ret(); } + // PyWeakref_GetRef returns a strong reference; handle<> steals it. + object callable{handle<>(rawCallable)}; return TfPyCall(callable)(args...); } }; @@ -68,12 +70,15 @@ struct TfPyFunctionFromPython // Attempt to get the referenced self parameter, then build a new // instance method and call it. TfPyLock lock; - PyObject *self = PyWeakref_GetObject(weakSelf.ptr()); - if (self == Py_None) { + PyObject *self = NULL; + if (PyWeakref_GetRef(weakSelf.ptr(), &self) <= 0) { TF_WARN("Tried to call a method on an expired python instance"); return Ret(); } + // PyWeakref_GetRef returns a strong reference to self; PyMethod_New + // takes its own reference, so release ours afterward. object method(handle<>(PyMethod_New(func.ptr(), self))); + Py_DECREF(self); return TfPyCall(method)(args...); } }; diff --git a/pxr/base/tf/pyIdentity.cpp b/pxr/base/tf/pyIdentity.cpp index 5389d8f25..06b7f9b0c 100644 --- a/pxr/base/tf/pyIdentity.cpp +++ b/pxr/base/tf/pyIdentity.cpp @@ -136,7 +136,15 @@ PyObject * Tf_PyIdHandle::Ptr() const { if (_weakRef) { TfPyLock lock; - return PyWeakref_GetObject(_weakRef); + PyObject *obj = NULL; + if (PyWeakref_GetRef(_weakRef, &obj) > 0) { + // Return a borrowed-style reference: decrement so the caller + // does not need to release. Safe because the GIL is held by + // all callers. + Py_DECREF(obj); + return obj; + } + return Py_None; // dead referent → match original behaviour } return 0; } diff --git a/pxr/base/tf/pyWeakObject.cpp b/pxr/base/tf/pyWeakObject.cpp index 8cfddc729..ecdd7f705 100644 --- a/pxr/base/tf/pyWeakObject.cpp +++ b/pxr/base/tf/pyWeakObject.cpp @@ -115,9 +115,15 @@ Tf_PyWeakObject::GetOrCreate(pxr_boost::python::object const &obj) pxr_boost::python::object Tf_PyWeakObject::GetObject() const { - return pxr_boost::python::object - (pxr_boost::python::handle<> - (pxr_boost::python::borrowed(PyWeakref_GetObject(_weakRef.get())))); + PyObject *obj = NULL; + if (PyWeakref_GetRef(_weakRef.get(), &obj) > 0) { + // PyWeakref_GetRef returns a strong reference; handle<> steals it. + return pxr_boost::python::object( + pxr_boost::python::handle<>(obj)); + } + // Dead referent: return Py_None explicitly, as the original did. + return pxr_boost::python::object( + pxr_boost::python::handle<>(pxr_boost::python::borrowed(Py_None))); } void -- 2.55.0