-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
MAINT: Deprecated PyObject_Compare in favor of PyObject_RichCompareBool. Fixes #6265 #6269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
PyObject *zero = PyLong_FromLong(0); | ||
PyObject *one = PyLong_FromLong(1); | ||
PyObject *minus_one = PyLong_FromLong(-1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either one
or minus_one
is not going to be returned, and should therefore have its refcount decreased before returning, similarly to what happens with zero
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So should I decrease both one
and minus_one
's refcount in the end ?
Like
if (is_gt == 1) {
ret = one;
} else if (is_eq == 1) {
ret = zero;
} else {
ret = minus_one;
}
if (PyErr_Occurred()) {
Py_DECREF(zero);
return;
}
Py_XDECREF(*out);
*out = ret;
}
Py_DECREF(zero);
Py_DECREF(one);
Py_DECREF(minus_one);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All except the one you assign to out
.
@jaimefrio I didn't quite understand the test part, would I be checking for #6229 problem in the test case ? |
@jaimefrio For reference count, I removed the |
The body of the
Feel free to rewrite it as you see fit, but beware of the subtleties: if we cannot get a positive comparison using |
@@ -325,7 +325,7 @@ PyObject_Cmp(PyObject *i1, PyObject *i2, int *cmp) | |||
{ | |||
int v; | |||
v = PyObject_RichCompareBool(i1, i2, Py_LT); | |||
if (v == 0) { | |||
if (v == 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two more instances of this mistake, right after the next two calls to PyObject_RichCompareBool
below.
@jaimefrio I have the changes as suggested by you. Thanks for the help. Are there any other things I need to do ? |
if (PyErr_Occurred()) { | ||
|
||
/* In case of error in comparison */ | ||
if ( v == -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra space inside parenthesis.
@jaimefrio Extra space around parenthesis removed. |
if (v == 1) { | ||
ret = PyLong_FromLong(0); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is a loophole in this logic. Consider the following:
>>> a = np.array([np.nan], dtype=object)
>>> a < 0
array([False], dtype=bool)
>>> a == 0
array([False], dtype=bool)
>>> a > 0
array([False], dtype=bool)
If we have an object that is neither larger, nor smaller, nor equal to 0
, right now we will set the output to NULL
, which I am not sure is what we really want. Perhaps None
is the right thing to set it to, but I'll ask on the list for the community wisdom on this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaimefrio I read a bit about NaN. In my opinion, comparing integers with NaN should return False
as the comparison operators result is a boolean, and the comparison wouldn't be True
.
Also, in JavaScript, all integer comparisons with NaN evaluates to false
.
Well, that was my opinion. If it is decided to change the result to None
as per the community decision,do keep updated on this one.
@jaimefrio Hi Jaime, do you have any updates for this PR ? |
v = PyObject_RichCompareBool(in1, zero, Py_EQ); | ||
if (v == 1) { | ||
ret = PyLong_FromLong(0); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so according to what seems to be the closest to a consensus, here we should have an else if (v == 0)
that raises a TypeError
, not sure what the right message should be. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaimefrio I have already implemented the above and its working as expected. For the error message, I was gonna ask you, what it should be 😛 .
How about we go with the error message you suggested in the mailing list or how about we just explicitly tell the user that the exception is due to NaN. Like TypeError: Trying to compare NaN
(Something like this).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaimefrio Any thoughts on the error message or should I send a PR with error message TypeError: unorderable types ...etc
@jaimefrio Any feedback for the error message ? |
@jaimefrio Can you please take a look at the new commit ? |
@charris Hi Charles, it seems that Jaime is busy these days and I couldn't get any feedback on this PR. Can you please take a look at this. |
@charris @jaimefrio How about merging this PR, its been pending for days. Is there any work required by my side to make it merge ready ? |
Just to note, there is a possibility of overhauling However, this PR also has good cleanups for |
v = PyObject_RichCompareBool(in1, zero, Py_LT); | ||
if (v == 1) { | ||
ret = PyLong_FromLong(-1); | ||
} else if (v == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be inclined to do this like
v = PyObject_RichCompareBool(in1, zero, Py_LT);
if (v == 1) {
ret = PyLong_FromLong(-1);
goto done;
}
else if (v == -1) {
goto error;
}
and repeat with minor variations for the other two cases. That avoids the deep indentations and is, IMHO, easier to follow.
Note the style for else
.
@charris I have made the changes as per your suggestions. |
if (v == 1) { | ||
ret = PyLong_FromLong(-1); | ||
goto done; | ||
} else if (v == -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note style guide doc/C_STYLE_GUIDE.rst.txt
. The else should be like
}
else if {
@charris I have modified the code as required. I hope its all good now. |
I apologize for getting a bit confused myself. I'll take another look later this evening. |
Py_XDECREF(*out); | ||
*out = ret; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong endentation.
LGTM modulo nitpick. Another possibility that adds no net lines and get rid of the goto is to replace
by
etc. |
@charris Changes updated. I hope its good now. |
OK, my bad. The other errors need checking. How about
|
@charris I have updated the code. Hope its all good to go now. |
Py_DECREF(zero); | ||
return; | ||
|
||
v = PyObject_RichCompareBool(in1, zero, Py_LT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need this line.
Almost there. When you are ready, squash the commits using |
@charris All commits squashed into one. |
LGTM pending tests. |
MAINT: Deprecated PyObject_Compare in favor of PyObject_RichCompareBool. Fixes #6265
Thanks @yashmehrotra . If you would like to make a PR against the |
I've backported the |
@jaimefrio I made the changes as discussed in #6265 . I ran all tests for Python2 and Python3 through
python runtests.py
/python3 runtests.py
and did not encounter any failure.As far as the
PyObject_Cmp
function innpy_3kcompat.h
goes, I did not change that. I although did fix the only part of code base that uses it withPyObject_RichCompareBool
.As far as
PyObject_Cmp
goes, should I remove it or add a deprecation warning ?