Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Lib/test/test_sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ def test_unsafe_tuple_compare(self):
check_against_PyObject_RichCompareBool(self, [float('nan')]*100)
check_against_PyObject_RichCompareBool(self, [float('nan') for
_ in range(100)])

def test_not_all_tuples(self):
self.assertRaises(TypeError, [(1.0, 1.0), (False, "A"), 6].sort)
#==============================================================================

if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a segfault occuring when sorting a list of heterogeneous values. Patch
contributed by Rémi Lapeyre.
24 changes: 14 additions & 10 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
lo.keys[i]);

if (key->ob_type != key_type) {
keys_are_in_tuples = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
keys_are_in_tuples = 0;

Copy link
Contributor

Choose a reason for hiding this comment

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

We remove this line, which over-compensates in the case you mentioned.

keys_are_all_same_type = 0;
break;
}
Expand Down Expand Up @@ -2338,21 +2339,24 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
else {
ms.key_compare = safe_object_compare;
}

if (keys_are_in_tuples) {
/* Make sure we're not dealing with tuples of tuples
* (remember: here, key_type refers list [key[0] for key in keys]) */
if (key_type == &PyTuple_Type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (key_type == &PyTuple_Type) {
if ( (keys_are_all_same_type == 0) || (key_type == &PyTuple_Type) ) {

Copy link
Contributor

Choose a reason for hiding this comment

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

And instead modify this check, which sets tuple_elem_compare to safe_object_compare in the case of nested tuples, to also fall back when the tuple first-elements are heterogeneous.

Copy link
Author

Choose a reason for hiding this comment

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

I don't think that's going to work because of the early exit at https://github.com/python/cpython/pull/12209/files#diff-5037da2a492e5903f100107e0506f818R2310 you can't be sure that all keys are tuples anyway.

I think the simplest way forward would be to use two variables key_type and inner_key_type to know if it's item[0] or item that changed type.

Does this make sense?

ms.tuple_elem_compare = safe_object_compare;
}
else {
ms.tuple_elem_compare = ms.key_compare;
}

ms.key_compare = unsafe_tuple_compare;
}
}
else {
ms.key_compare = safe_object_compare;
}

if (keys_are_in_tuples) {
/* Make sure we're not dealing with tuples of tuples
* (remember: here, key_type refers list [key[0] for key in keys]) */
if (key_type == &PyTuple_Type)
ms.tuple_elem_compare = safe_object_compare;
else
ms.tuple_elem_compare = ms.key_compare;

ms.key_compare = unsafe_tuple_compare;
}
}
/* End of pre-sort check: ms is now set properly! */

Expand Down