Skip to content

GH-145247: Use _PyTuple_FromPair[Steal] in Objects#145884

Open
sergey-miryanov wants to merge 5 commits intopython:mainfrom
sergey-miryanov:feat/145247-pytuple-from-pair-use-2
Open

GH-145247: Use _PyTuple_FromPair[Steal] in Objects#145884
sergey-miryanov wants to merge 5 commits intopython:mainfrom
sergey-miryanov:feat/145247-pytuple-from-pair-use-2

Conversation

@sergey-miryanov
Copy link
Contributor

@sergey-miryanov sergey-miryanov commented Mar 12, 2026

It is a second PR for usage of _PyTuple_FromPair[Steal] in the codebase.

result = _PyTuple_FromPairSteal(is_attr_obj, obj);
return result;

done:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change done to error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

result = _PyTuple_FromPairSteal(first_obj, (PyObject *)it);
return result;

done:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ);
Py_DECREF(val1);
if (to_delete < 0) {
goto error;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this safe? If goto error happens, won't it decref twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, it is not safe. I don't understand why I did this (blame on me).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -1439,7 +1439,7 @@ list_extend_dictitems(PyListObject *self, PyDictObject *dict)
Py_ssize_t i = 0;
PyObject *key_value[2];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this to key, value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Contributor Author

@sergey-miryanov sergey-miryanov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address review

result = _PyTuple_FromPairSteal(is_attr_obj, obj);
return result;

done:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

result = _PyTuple_FromPairSteal(first_obj, (PyObject *)it);
return result;

done:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -1439,7 +1439,7 @@ list_extend_dictitems(PyListObject *self, PyDictObject *dict)
Py_ssize_t i = 0;
PyObject *key_value[2];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces a leak in the dict type (see my comment below about a leak).

Comment on lines +1187 to +1188
result = _PyTuple_FromPairSteal(is_attr_obj, obj);
return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the result variable in this scope and replace return result with return NULL in the error path. By the way, previously this function had two result variables!

Suggested change
result = _PyTuple_FromPairSteal(is_attr_obj, obj);
return result;
return _PyTuple_FromPairSteal(is_attr_obj, obj);

Comment on lines +1279 to +1280
result = _PyTuple_FromPairSteal(first_obj, (PyObject *)it);
return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same remark, you can now remove the result variable to simplify the code.

Suggested change
result = _PyTuple_FromPairSteal(first_obj, (PyObject *)it);
return result;
return _PyTuple_FromPairSteal(first_obj, (PyObject *)it);

Py_DECREF(pair);
}
Py_DECREF(key);
Py_XDECREF(val1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removal is suspicious, since there is still Py_INCREF(val1); above.

The problem is that test_dict uses many immortal objects, so they can fail to detect refleaks.

With this patch, I can trigger a leak using ./python -m test -v test_dict -m test_dictview_set_operations_on_items -R 3:3:

diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index b2f4363b23e..4f2b6e1dc14 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -804,6 +804,11 @@ def test_dictview_set_operations_on_items(self):
         self.assertEqual(k1 ^ k2, {(3,3)})
         self.assertEqual(k1 ^ k3, {(1,1), (2,2), (4,4)})
 
+        obj = object()
+        d = {obj: b'abc'.decode()}.items()
+        d2 = {obj: b'def'.decode()}.items()
+        d ^ d2
+
     def test_items_symmetric_difference(self):
         rr = random.randrange
         for _ in range(100):

Comment on lines +229 to 230
result = _PyTuple_FromPairSteal(next_index, next_item);
return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = _PyTuple_FromPairSteal(next_index, next_item);
return result;
return _PyTuple_FromPairSteal(next_index, next_item);

Comment on lines +272 to 273
result = _PyTuple_FromPairSteal(next_index, next_item);
return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = _PyTuple_FromPairSteal(next_index, next_item);
return result;
return _PyTuple_FromPairSteal(next_index, next_item);

Comment on lines +4889 to 4890
z = _PyTuple_FromPairSteal((PyObject *)div, (PyObject *)mod);
return z;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
z = _PyTuple_FromPairSteal((PyObject *)div, (PyObject *)mod);
return z;
return _PyTuple_FromPairSteal((PyObject *)div, (PyObject *)mod);

The z variable is no longer needed.

Comment on lines +6180 to 6181
result = _PyTuple_FromPairSteal((PyObject *)quo, (PyObject *)rem);
return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = _PyTuple_FromPairSteal((PyObject *)quo, (PyObject *)rem);
return result;
return _PyTuple_FromPairSteal((PyObject *)quo, (PyObject *)rem);

Comment on lines +6363 to 6364
ratio_tuple = _PyTuple_FromPairSteal(numerator, _PyLong_GetOne());
return ratio_tuple;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ratio_tuple = _PyTuple_FromPairSteal(numerator, _PyLong_GetOne());
return ratio_tuple;
return _PyTuple_FromPairSteal(numerator, _PyLong_GetOne());

Comment on lines +1174 to 1175
item = _PyTuple_FromPairSteal(key, value);
return item;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
item = _PyTuple_FromPairSteal(key, value);
return item;
return _PyTuple_FromPairSteal(key, value);

if (result == NULL) {
Py_DECREF(key);
Py_DECREF(value);
goto done;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange to call this label "done" whereas it's only used for error path. It should be called "error".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants