-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
BUG/ENH: Fix use of ndpointer in return values #12431
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
This matches the ``.contents`` member of normal ctypes arrays, and can be used | ||
to construct an ``np.array`` around the pointers contents. | ||
|
||
This replaces ``np.array(some_nd_pointer)``, which stopped working in 1.15. |
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.
On the one hand, we could try to restore this - on the other, it didn't make a massive amount of sense anyway.
full_dtype = _dtype((self._dtype_, self._shape_)) | ||
full_ctype = ctypes.c_char * full_dtype.itemsize | ||
buffer = ctypes.cast(self, ctypes.POINTER(full_ctype)).contents | ||
return frombuffer(buffer, dtype=full_dtype).squeeze(axis=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.
This is sort of frustrating - there ought to be a better way to construct an array from a pointer without having to go through an intermediate byte array.
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.
That's why the full_ctype
?
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.
Yeah, the goal of full_ctypes is to get an object supporting the buffer protocol that we can pass to frombuffer
. Perhaps ndarray.from_address(addr, shape, strides, dtype)
ought to be a (dangerous) part of the C 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.
Interesting thought. I'd mostly be concerned if memory or speed was a problem with the current approach.
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.
Memory shouldn't be a concern - this doesn't make any copies of the underlying data - at most we're churning around a bunch of metadata. _CPointer.contents
returns a reference, not a copy.
if hasattr(sys, 'gettotalrefcount'): | ||
try: | ||
cdll = load_library('_multiarray_umath_d', np.core._multiarray_umath.__file__) | ||
except OSError: | ||
pass | ||
try: | ||
test_cdll = load_library('_multiarray_tests', np.core._multiarray_tests.__file__) |
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.
Long lines here and above.
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.
Missed this one. It's not clear to me why load_library
is needed here - the tests in cpython just use ctypes.CDLL(np.core._multiarray_tests.__file__)
, and presumably work just fine.
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.
Are you looking to fix this, or are you going to try the cpython approach and remove an argument?
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.
Oops, load_library
is in the public interface, forgot about that.
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.
Given the lines in this section were long before this patch, I'd be inclined leave things as they are, and not spend more CI cycles on whitespace changes :).
In a future patch I might try using CDLL directly here and seeing if any CI breaks.
Couple of style nits. Otherwise LGTM. Is there any reason someone might have been using the private |
I assume you mean using it by name, rather than using it as the return value of I had a quick look at whether splitting off If you're still worried, I could rename |
This: * fixes a regression in 1.15, where it became impossible to set the return value of a cdll function to an ndpointer. * removes ndpointer.__array_interface__, which was being ignored anyway in favor of the PEP3118 buffer protocol * adds `ndpointer.contents` to recover the lost functionality, while staying in line with the ctypes behavior * removes another instance of `descr`, which enables overlapping fields to be returned from C functions (such as unions). * Fixes a long-term bug where using ndpointer as a return type without specifying both type and dtype would produce an object array containing a single `ndpointer`. Now the ndpointer is returned directly. This relates to numpygh-12421, and likely fixes toinsson/pyrealsense#82
1056f0d
to
73322a0
Compare
Style nits addressed - let me know if any remain |
I'm not particularly worried, just trying to game might go wrong, I was mostly thinking to the missing |
I'd be surprised if anyone is using A bigger question is whether we still need to fix gh-12421 - perhaps we have the priority of Should we be viewing PEP3118 as a replacement for |
I don't really have an opinion as I haven't used the ctypes functionality. The new |
The P has always been there - it's the PEP3118 buffer format for What changed in 1.15 is that instead of ignoring unsupported or corrupt PEP3118 formats and trying something else, we raise an exception. Previously that something else was |
Hmm, from what you say #12421 should be a "won't fix". Is there a backwards compatible workaround? |
Depends what we want to be compatible with. If the goal is simply to make
|
Correct, that's the option I'm leaning towards. I suspect no one actually cares anyway - the only downstream issue I found is resolved by this PR. |
I was thinking about |
What I was asking though, was whether |
Used to achieve what? #10882 made it impossible to override |
Can't say, as the the actual code was that led to the issue isn't known. The example is artificial, after all the array is already available :) So what is the actual problem downstream, and can it be worked around. |
@charris: Ask who? By "the issue", are you referring to toinsson/pyrealsense#82? Because if so, the cause is using |
Yes, the question was about toinsson/pyrealsense#82 and concerns whether or not they can work around the issue for the versions of NumPy that were failing. |
In that case, yes, they can work around it by copying my The alternative would be for us to backport this PR to 1.15.x, but that might not be worth it at this point |
Well, let's get this in. Thanks Eric. |
I was concerned about a workaround that would not only fix the problem, but work for all earlier numpy versions so they didn't end up with version dependent code. Sorry that it has taken me a bit of trouble to correctly express the concern. Anyway, sounds like that is possible. |
Yes, I believe that |
I have ctypes objects that implement both buffer protocol and |
I suspect the buffer protocol takes precedence, but you might find that |
This:
ndpointer.__array_interface__
, which was being ignored anyway in favor of the PEP3118 buffer protocolndpointer.contents
to recover the lost functionality, while staying in line with the ctypes behaviordescr
, which enables overlapping fields to be returned from C functions (such as unions).ndpointer
. Now the ndpointer is returned directly.This relates to (but does not fix) gh-12421, and likely fixes toinsson/pyrealsense#82