-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
MAINT: Message value of NPY_FEATURE_VERSION
used
#24861
Conversation
It might also be useful to bake this value into binaries for validating the binaries themselves. One option to do this might be the approach outlined in this SO answer. Maybe there are better ways to accomplish this. |
NPY_FEATURE_VERSION
usedNPY_FEATURE_VERSION
used
NPY_FEATURE_VERSION
usedNPY_FEATURE_VERSION
used
cc @seberg (in case you have thoughts on 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.
Thanks, good idea. I noticed that MSVC needs parentheses, however, I think the string concatenation doesn't actually work here? We could stringify it via some macros, although unfortunatly, I don't think the feature version has convenient to understand values.
Maybe the best is to just hardcode the 1.19 API version (since its defined just above) and for the other one stress that it was set to the user provided NPY_TARGET_VERSION
. (can still stringify it, although its unfortunately not clear to read)
To provide a bit more visibility to how extensions using NumPy are built, message the value of `NPY_FEATURE_VERSION` used during the build so that this can be more easily seen by builders trying to validate build behavior.
Unfortunately, clang at least on MacOS really insists on a string literal, so that the macro expansion has to be done explicitly.
ea6e860
to
d816fbd
Compare
I pushed a fix. Now running with it, I see two things:
I still like the idea, the fact that it shows as a I had talked with @jakirkham he would like to be able to see/check if things are compiled correctly. Two ideas came up:
|
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 agree that some info like this is nice to have when reading build logs of downstream packages. However, using a pragma to emit messages from C code directly cannot be right. That's not only true in this instance, this should never be done for something that is for info rather than a warning.
It also seems to me that you do not want to see this message per build target. Once per extension module is far too much - these kinds of things are incredibly noisy every time Cython does something like this. In the end, the Python package that is being built has a single ABI constraint baked in - the minimum numpy
version that, as a whole, it is compatible with.
If you look at NumPy's own build log, this info should not be there at all I think, since it's not relevant. In downstream packages, it belongs in the configure stage output, not the compile output.
That minimum numpy
value is already needed for the package - because it needs to write out the correct constraint in its runtime dependencies. At present I think almost no package gets that right (I haven't even thought about doing this for SciPy yet). Isn't that the more important thing to figure out? And then the right info is much more easily accessible.
It might also be useful to bake this value into binaries for validating the binaries themselves.
Isn't this present already? There should be an ABI version check that makes use of this.
Well, clearly it's baked in somewhere, but the only clear way to get to it is to attempt an import and see the failure (a thought now, is that you could fake the NumPy version to be 0, then the error message will print out which version you compiled for, although its the hex version unfortunately). So, I think the ask/question is that we could bake it in so that it is easy to check a conda recipy (or wheel) for whether it was compiled with the correct support range, without necessarily having to run the full tests against the oldest NumPy version. |
Since extension modules don't have to have public symbols being PyInit, a failed import when trying with version 0 and parsing the message from that may be a good option? If there was a simple separate utility for that, it would also work on existing extension modules, not just future ones. |
Yeah, that could be a "challenge accepted" for someone, not feeling it right now. One problem is that library X may import SciPy first, so you may need to target the import to test or inspect the frame to make it reliable.
We could injecting public/hidden symbols in principle whenever |
The other thing that would be useful is to query the compatible version at build time. From https://numpy.org/devdocs/dev/depending_on_numpy.html#adding-a-dependency-on-numpy: By default, NumPy will expose an API that is backwards compatible with the oldest NumPy version that supports the currently oldest compatible Python version. That can of course be hardcoded in the downstream package, but it's annoying logic to maintain. I think there may not be a better way than reading That same doc link already says: This define must be consistent for each extension module (use of
I think it's not hard to do. Making the wording of that line useful and not confusing is a little tricky - I'd be happy to hear better ideas. |
Okay, I got that mostly working in the SciPy build log now. With numpy 1.25.2 it shows:
and with numpy 1.24.3:
The code to implement that: # This is the minimum supported NumPy version from a binary compatibility
# perspective. If we ever need to set NPY_TARGET_VERSION, it should be defined
# here, taken into account for the message below, and added to all extension
# modules. `NPY_FEATURE_VERSION` first became available in NumPy 1.25.0.
_np_feature_version = cc.get_define('NPY_FEATURE_VERSION',
include_directories: inc_np, prefix: '#include "numpy/numpyconfig.h"'
)
if _np_feature_version == ''
# We're dealing with numpy <1.25.0 here. So get API version instead
_np_feature_version = cc.get_define('NPY_API_VERSION',
include_directories: inc_np, prefix: '#include "numpy/numpyconfig.h"'
)
endif
_np_target_version = {
'0x0000000d': '1.19.0',
'0x0000000e': '1.20.0', # also 1.21.0 (no API additions there)
'0x0000000f': '1.22.0',
'0x00000010': '1.23.0', # also 1.24.0
}.get(_np_feature_version)
message('Targeted NumPy C API version: ', _np_target_version) The annoyance here is of course the translation of hex API numbers to string versions. We can't know what the hex numbers for future numpy release will be. To avoid that problem, we need a string equivalent like: #define NPY_FEATURE_VERSION_STRING If we'd have that, I think the above code works - and will reduce down to a single Does this address the use case, and if so should we add such a define? |
I don't think it's been laid out explicitly, but the use-case in conda-forge is that we need to be able to set a global baseline in conda-forge for things to work in general. If there was no numpy 2.0 coming up, it's likely we'd just keep stepping through the numpy versions according to NEP29, and accepting their defaults (even though that'd be well more conservative than what our metadata claims; e.g. building against numpy 1.25 would generate a However, to avoid duplicating our build matrix between numpy 1 & 2, it's very tempting to use the Realistically, something showing up in the log is likely not good enough for that. |
Right was alluding to the need for some ability to inspect binaries above Maybe there are better ways than what I proposed there |
Given that context Ralf, how should we proceed? |
Given the discussion above, I think the way forward is:
And close this PR - turning it into an issue with feature requests. |
To provide a bit more visibility to how extensions using NumPy are built, message the value of
NPY_FEATURE_VERSION
used during the build so that this can be more easily seen by builders trying to validate build behavior.