Fix leak when directly iterating feature in PyQGIS

Fixes #32944
This commit is contained in:
Nyall Dawson 2025-02-21 10:45:20 +10:00 committed by github-actions[bot]
parent 1959776089
commit 5b1c100f7f
4 changed files with 42 additions and 0 deletions

View File

@ -40,6 +40,9 @@ geometry and a list of field/values attributes.
QgsAttributes attributes = sipCpp->attributes();
PyObject *attrs = sipConvertFromType( &attributes, sipType_QgsAttributes, Py_None );
sipRes = PyObject_GetIter( attrs );
// PyObject_GetIter has added a ref to attrs - we need to decrement the ref from sipConvertFromType,
// so that the garbage collector will delete attrs when the iterator is deleted
Py_DECREF( attrs );
%End

View File

@ -40,6 +40,9 @@ geometry and a list of field/values attributes.
QgsAttributes attributes = sipCpp->attributes();
PyObject *attrs = sipConvertFromType( &attributes, sipType_QgsAttributes, Py_None );
sipRes = PyObject_GetIter( attrs );
// PyObject_GetIter has added a ref to attrs - we need to decrement the ref from sipConvertFromType,
// so that the garbage collector will delete attrs when the iterator is deleted
Py_DECREF( attrs );
%End
SIP_PYOBJECT __getitem__( int key ) /HoldGIL/;

View File

@ -76,6 +76,9 @@ class CORE_EXPORT QgsFeature
QgsAttributes attributes = sipCpp->attributes();
PyObject *attrs = sipConvertFromType( &attributes, sipType_QgsAttributes, Py_None );
sipRes = PyObject_GetIter( attrs );
// PyObject_GetIter has added a ref to attrs - we need to decrement the ref from sipConvertFromType,
// so that the garbage collector will delete attrs when the iterator is deleted
Py_DECREF( attrs );
% End
#endif

View File

@ -320,6 +320,39 @@ class TestQgsVectorLayer(QgisTestCase, FeatureSourceTestCase):
myCount = myLayer.featureCount()
self.assertEqual(myCount, 6)
def test_attribute_iteration(self):
layer = QgsVectorLayer(
self.get_test_data_path("lines.shp").as_posix(), "Lines", "ogr"
)
self.assertTrue(layer.isValid())
all_attrs = [
[attr for attr in feat.attributes()] for feat in layer.getFeatures()
]
self.assertCountEqual(
all_attrs,
[
["Highway", 1.0],
["Highway", 1.0],
["Arterial", 2.0],
["Arterial", 2.0],
["Arterial", 2.0],
["Arterial", 2.0],
],
)
all_attrs = [[attr for attr in feat] for feat in layer.getFeatures()]
self.assertCountEqual(
all_attrs,
[
["Highway", 1.0],
["Highway", 1.0],
["Arterial", 2.0],
["Arterial", 2.0],
["Arterial", 2.0],
["Arterial", 2.0],
],
)
# undo stack
def testUndoStack(self):
layer = createLayerWithOnePoint()