From 3c09beb3b1f785c920eed3d61f7c2a2a06deba50 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 14 May 2021 19:18:50 +1000 Subject: [PATCH] Fix memory leak in IDPropertyGroup.pop() When popping ID-property groups/arrays, ID-property was removed but not freed. Now the value is converted to a native Python type and freed. --- source/blender/python/generic/idprop_py_api.c | 4 ++-- source/blender/python/generic/idprop_py_api.h | 1 + source/blender/python/intern/bpy_rna.c | 9 +++++++-- tests/python/bl_pyapi_idprop.py | 6 +++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 75fd63e16f9..fc7054f675a 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -766,7 +766,7 @@ static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self) } /* for simple, non nested types this is the same as BPy_IDGroup_WrapData */ -static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) +PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) { switch (prop->type) { case IDP_STRING: @@ -919,7 +919,7 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args) return NULL; } - IDP_RemoveFromGroup(self->prop, idprop); + IDP_FreeFromGroup(self->prop, idprop); return pyform; } diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h index 991ae7be9c9..4cccea3a936 100644 --- a/source/blender/python/generic/idprop_py_api.h +++ b/source/blender/python/generic/idprop_py_api.h @@ -60,6 +60,7 @@ PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop); PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop); int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val); +PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop); PyObject *BPy_IDGroup_WrapData(struct ID *id, struct IDProperty *prop, struct IDProperty *parent); bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *group, PyObject *ob); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 354aa9b6986..1711637458a 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -5006,8 +5006,13 @@ static PyObject *pyrna_struct_pop(BPy_StructRNA *self, PyObject *args) idprop = IDP_GetPropertyFromGroup(group, key); if (idprop) { - PyObject *ret = BPy_IDGroup_WrapData(self->ptr.owner_id, idprop, group); - IDP_RemoveFromGroup(group, idprop); + /* Don't use #BPy_IDGroup_WrapData as the id-property is being removed from the ID. */ + PyObject *ret = BPy_IDGroup_MapDataToPy(idprop); + /* Internal error. */ + if (UNLIKELY(ret == NULL)) { + return NULL; + } + IDP_FreeFromGroup(group, idprop); return ret; } } diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py index 3d0cbd2a7bb..7b480f5fa16 100644 --- a/tests/python/bl_pyapi_idprop.py +++ b/tests/python/bl_pyapi_idprop.py @@ -15,12 +15,12 @@ class TestHelper: def setUp(self): self._id = bpy.context.scene - assert(len(self._id.keys()) == 0 or self._id.keys() == ["cycles"]) + self._id.pop("cycles", None) + assert(len(self._id.keys()) == 0) def tearDown(self): for key in list(self._id.keys()): - if key != "cycles": - del self._id[key] + del self._id[key] def assertAlmostEqualSeq(self, list1, list2): self.assertEqual(len(list1), len(list2))