From 19111f1ed708039e721f2d6ea741ca66d8cc7204 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 14 Feb 2017 16:38:38 +0100 Subject: [PATCH] issue #2902: track attribute objects in parent structure to notify about changes --- src/Base/PyObjectBase.cpp | 60 +++++++++++++++++++++++++++++++++++---- src/Base/PyObjectBase.h | 5 +++- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/Base/PyObjectBase.cpp b/src/Base/PyObjectBase.cpp index a3571ba90d5e..643892a081eb 100644 --- a/src/Base/PyObjectBase.cpp +++ b/src/Base/PyObjectBase.cpp @@ -139,11 +139,23 @@ PyObject* PyObjectBase::__getattr(PyObject * obj, char *attr) return NULL; } + // If an attribute references this as parent then reset it (bug #0002902) + PyObject* cur = pyObj->getTrackedAttribute(attr); + if (cur) { + if (PyObject_TypeCheck(cur, &(PyObjectBase::Type))) { + PyObjectBase* base = static_cast(cur); + base->resetAttribute(); + pyObj->untrackAttribute(attr); + } + } + PyObject* value = pyObj->_getattr(attr); #if 1 if (value && PyObject_TypeCheck(value, &(PyObjectBase::Type))) { - if (!static_cast(value)->isConst()) + if (!static_cast(value)->isConst()) { static_cast(value)->setAttributeOf(attr, pyObj); + pyObj->trackAttribute(attr, value); + } } else if (value && PyCFunction_Check(value)) { // ExtensionContainerPy::initialization() transfers the methods of an @@ -180,13 +192,13 @@ int PyObjectBase::__setattr(PyObject *obj, char *attr, PyObject *value) // If an attribute references this as parent then reset it // before setting the new attribute - PyObject* cur = static_cast(obj)->_getattr(attr); + PyObject* cur = static_cast(obj)->getTrackedAttribute(attr); if (cur) { if (PyObject_TypeCheck(cur, &(PyObjectBase::Type))) { PyObjectBase* base = static_cast(cur); base->resetAttribute(); + static_cast(obj)->untrackAttribute(attr); } - Py_DECREF(cur); } int ret = static_cast(obj)->_setattr(attr, value); @@ -291,7 +303,7 @@ void PyObjectBase::resetAttribute() } } -void PyObjectBase::setAttributeOf(const char* attr, const PyObjectBase* par) +void PyObjectBase::setAttributeOf(const char* attr, PyObject* par) { if (!attrDict) { attrDict = PyDict_New(); @@ -300,7 +312,7 @@ void PyObjectBase::setAttributeOf(const char* attr, const PyObjectBase* par) PyObject* key = PyString_FromString("__attribute_of_parent__"); PyObject* attro = PyString_FromString(attr); PyDict_SetItem(attrDict, key, attro); - PyDict_SetItem(attrDict, attro, const_cast(par)); + PyDict_SetItem(attrDict, attro, par); Py_DECREF(attro); Py_DECREF(key); } @@ -315,7 +327,20 @@ void PyObjectBase::startNotify() if (attr) { PyObject* parent = PyDict_GetItem(attrDict, attr); if (parent) { + // Inside __setattr of the parent structure the 'attr' + // is being removed from the dict and thus its reference + // counter will be decremented. To avoid to be deleted we + // must tmp. increment it and afterwards decrement it again. + Py_INCREF(parent); + Py_INCREF(attr); + Py_INCREF(this); + __setattr(parent, PyString_AsString(attr), this); + + Py_DECREF(parent); // might be destroyed now + Py_DECREF(attr); // might be destroyed now + Py_DECREF(this); // might be destroyed now + if (PyErr_Occurred()) PyErr_Clear(); } @@ -323,3 +348,28 @@ void PyObjectBase::startNotify() Py_DECREF(key); } } + +PyObject* PyObjectBase::getTrackedAttribute(const char* attr) +{ + PyObject* obj = 0; + if (attrDict) { + obj = PyDict_GetItemString(attrDict, attr); + } + return obj; +} + +void PyObjectBase::trackAttribute(const char* attr, PyObject* obj) +{ + if (!attrDict) { + attrDict = PyDict_New(); + } + + PyDict_SetItemString(attrDict, attr, obj); +} + +void PyObjectBase::untrackAttribute(const char* attr) +{ + if (attrDict) { + PyDict_DelItemString(attrDict, attr); + } +} diff --git a/src/Base/PyObjectBase.h b/src/Base/PyObjectBase.h index 387c7c6cc945..467bd43d9263 100644 --- a/src/Base/PyObjectBase.h +++ b/src/Base/PyObjectBase.h @@ -296,8 +296,11 @@ class BaseExport PyObjectBase : public PyObject typedef void* PointerType; private: - void setAttributeOf(const char* attr, const PyObjectBase* par); + void setAttributeOf(const char* attr, PyObject* par); void resetAttribute(); + PyObject* getTrackedAttribute(const char* attr); + void trackAttribute(const char* attr, PyObject* obj); + void untrackAttribute(const char* attr); protected: std::bitset<32> StatusBits;