-
-
Notifications
You must be signed in to change notification settings - Fork 168
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
Added pg_IntFromObjEx(), pg_TwoIntsFromObjEx() and pg_IntFromSeqIndexEx() #1842
base: main
Are you sure you want to change the base?
Added pg_IntFromObjEx(), pg_TwoIntsFromObjEx() and pg_IntFromSeqIndexEx() #1842
Conversation
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.
LGTM 👍
I guess time will see if we get some good speed ups from these.
b3abd34
to
0282ff3
Compare
I get it going from (without this PR):
to (with this PR):
Seems about 10% faster all over. |
f9d789a
to
a9a27c3
Compare
- pg_IntFromObjEx - pg_IntFromSeqIndexEx - pg_TwoIntsFromObjEx
a9a27c3
to
aed8033
Compare
I'll be reverting the implementation in rect.collidepoint as an example because of the changes that were made to the rect module. |
This reverts commit aed8033.
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 see no harm in adding these into the internal pygame API for other PRs and developers to use. In the previous example usage (now removed from this PR because of other changes to Rect) there was a demonstrated 10% performance improvement using these functions over current implementations.
If using them doesn't turn out to provide a benefit long-term then developers won't use them and we could eventually clean them out of the developer API. Right now there seems to be a proven benefit and no change to our public facing API.
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.
Thank you for this PR. I have found some problems.
I use this test function:
static PyObject*
int_cvt_test(PyObject* self, PyObject *arg) // METH_O
{
int val;
if(!pg_IntFromObjEx(arg, &val, "This is an error msg.")){
return NULL;
}
printf("Got value %d\n", val);
Py_RETURN_NONE;
}
Problems
-
TypeError
if input object only has__int__
A
TypeError
is raised if the input object has__int__
or__float__
method but doesn't have__index__
method. (Such asdecimal.Decimal
andfractionsFraction
)Code
class Num2: def __int__(self): return 1 int_cvt_test(Num2()) # Should print "Got value 1"
Current behavior:
Traceback (most recent call last): File "x:\python\pygame_test\pgint.py", line 20, in <module> int_cvt_test(Num2()) # Got value 1 TypeError: This is an error msg.
From python 3.10+,
PyLong_AsLong
would no longer use__int__
to get the int value of object. This is the cause of the problem. In fact, this is also why you can't pass a float object toPyLong_AsLong
.
To check if object is able to be converted as int object, use PyNumber_Check. -
Some errors is still replaced with
TypeError
If any exception is raised inside of the
__index__()
method of the input object, aTypeError
is always thrown out.Code
class FooError(Exception): pass class Foo: def __index__(self): raise FooError("Fooooo.") int_cvt_test(Foo()) # Should raise "__main__.FooError: Fooooo."
Current behavior:
Traceback (most recent call last): File "x:\python\pygame_test\pgint.py", line 23, in <module> int_cvt_test(Foo()) # __main__.FooError: Fooooo. TypeError: This is an error msg.
Those exceptions should have a higher priority than the customized
TypeError
. -
Float input should also be able to cause
OverflowError
Code
int_cvt_test(1e44) # should raise OverflowError
Current behavior:
No error is raised.
Solution
I had written another version in #2405 before I found this PR.
In this version, you don't need to override any exception raised inside. (Override is not good)
Only when the input object can't pass the PyNumber_Check
, will the msg
argument be used.
I hope my code would be helpful for you.
static int
pg_IntFromObjEx(PyObject *obj, int *val, const char *msg)
{
int tmp_val;
PyObject *tmp_obj;
SDL_bool do_decref = SDL_FALSE;
if (PyLong_Check(obj)) {
tmp_obj = obj;
}
else if (PyNumber_Check(obj)) {
tmp_obj = PyNumber_Long(obj);
if (!tmp_obj)
return 0;
do_decref = SDL_TRUE;
}
else {
PyErr_SetString(PyExc_TypeError ,msg);
return 0; // Not a number type
}
tmp_val = PyLong_AsLong(tmp_obj);
if (do_decref)
Py_DECREF(tmp_obj);
if (tmp_val == -1 && PyErr_Occurred()) {
return 0;
}
*val = tmp_val;
return 1;
}
This PR is not ready for merge, so I'm marking it as a draft. |
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 problems I mentioned have been fixed. LGTM👍
Added three new C API functions:
These three C API functions are supposed to be slightly faster than the current ones and have a different behaviour as they raise errors on their own, something that their current couterparts do not do. Errors should be more precise and provide more information, epecifically about what the error is, what it expected and what it got in terms of value/type/numebr of values.
You can either use a custom error message to override the default ones or pass NULL to use the predefined error messages and formatting. I recommend passing NULL most of the time as it will give you more precise and informative error messages. Only use a custom message when you need to add more description.
Modified:
I'd like to higlight that in the case of collidepoint, passing a tupleof 2 items is about 83% faster and a list of 2 items about 38% faster. This could entail similar benefits for every function that prefers tuples/lists of 2 items.
Rect.collidepoint() main:
This PR:
I used this program to test the new collidepoint with the new C functions: