-
-
Notifications
You must be signed in to change notification settings - Fork 11.9k
BUG: Avoid errors on NULL during deepcopy #21996
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
HaoZeke
left a comment
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.
Will let @seberg be the last word but this seems solid. Thanks!
|
@seberg I realized when I sat back down to work on this, I wasn't exactly clear of how to setup a test for the fail case. I'm stumbling a little bit on how to create an object that will fail when def test__deepcopy__catches_failure(self):
class MyArray(np.ndarray):
def __deepcopy__(self, *args, **kwargs):
raise Exception
a = MyArray([1, 2, 3], dtype='O')
a.__deepcopy__({}) # Raises an `Exception` defined above, not one from within `_deepcopy_call` C codeI also tried mocking out def test__deepcopy__catches_failure(self):
a = np.array([1, 2, 3], dtype='O')
with patch.object(copy, 'deepcopy', side_effect=None):
b = a.__deepcopy__({})
# What would a valid assertion be here?
assert (b == None).all()Do you have any suggestions or examples from other places in the code of how to do this? |
|
@jcusick13 oh, you have to put hte bad object into the array: |
|
Thanks @seberg. I added the suggested changes but am still a little bit confused. From looking at the docs for
though it seems like the /* call deepcopy on this argument */
res = PyObject_CallFunctionObjArgs(deepcopy, itemp, visit, NULL);
Py_DECREF(itemp);
if (res == NULL) {
return -1;
} |
|
How exceptions work (typically), is that you return NULL indicating that an error occurred (somewhere). Python then sets the global error state that you could check for example with |
|
Ah thanks for the explanation @seberg. I think my final point of confusion is if we then expect a failure within this function to raise a Python Mainly, it's not clear to me what to check during the test for the fail case. What should I be looking to create an |
numpy/core/tests/test_multiarray.py
Outdated
| raise Exception | ||
|
|
||
| arr = np.array([1, MyObj(), 3], dtype='O') | ||
| arr.__deepcopy__({}) |
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.
This should float up the exception, so you could not use Exception above maybe (maybe AssertionError), not that it matters much. What matters is wrapping this into:
with pytest.raises(Exception): # (or a more exact Exception)
arr.__deepcopy__({})
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.
Got it! I think I was just overthinking things a bit. Just made the changes now; appreciate the help 🙌
|
Thanks also for that last error test! Hope to see you around :). Lets put this in, it looks nice to me. |
Addresses concerns raised in the two linked issues by: Casting NULL objects to Py_None within _deepcopy_call to avoid issues within CPython's deepcopy Setting _deepcopy_call to return an int code for error/success, which is then checked in array_deepcopy Closes numpygh-8706, numpygh-21883
Closes #8706, #21883
Addresses concerns raised in the two linked issues by:
NULLobjects toPy_Nonewithin_deepcopy_callto avoid issues within CPython'sdeepcopy_deepcopy_callto return anintcode for error/success, which is then checked inarray_deepcopy