pydata / numexpr

Fast numerical array expression evaluator for Python, NumPy, Pandas, PyTables and more
https://numexpr.readthedocs.io/en/latest/user_guide.html
MIT License
2.25k stars 212 forks source link

BLD: build with numpy instead of oldest-supported-numpy #478

Closed neutrinoceros closed 8 months ago

neutrinoceros commented 8 months ago

I noticed while testing a downstream package that the following ImportError was raised from NumPy 2.0.0rc1 pointing to numexpr:

Traceback ```python-traceback A module that was compiled using NumPy 1.x cannot be run in NumPy 2.1.0.dev0 as it may crash. To support both 1.x and 2.x versions of NumPy, modules must be compiled with NumPy 2.0. Some module may need to rebuild instead e.g. with 'pybind11>=2.12'. If you are a user of the module, the easiest solution will be to downgrade to 'numpy<2' or try to upgrade the affected module. We expect that some modules will need time to support NumPy 2. Traceback (most recent call last): File "/opt/hostedtoolcache/Python/3.12.2/x64/bin/pytest", line 8, in sys.exit(console_main()) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/config/__init__.py", line 197, in console_main code = main() File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/config/__init__.py", line 174, in main ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_hooks.py", line 501, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_manager.py", line 119, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_callers.py", line 102, in _multicall res = hook_impl.function(*args) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 332, in pytest_cmdline_main return wrap_session(config, _main) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 285, in wrap_session session.exitstatus = doit(config, session) or 0 File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 338, in _main config.hook.pytest_collection(session=session) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_hooks.py", line 501, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_manager.py", line 119, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_callers.py", line 102, in _multicall res = hook_impl.function(*args) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 349, in pytest_collection session.perform_collect() File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 813, in perform_collect self.items.extend(self.genitems(node)) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 981, in genitems yield from self.genitems(subnode) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 981, in genitems yield from self.genitems(subnode) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 976, in genitems rep, duplicate = self._collect_one_node(node, handle_dupes) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/main.py", line 839, in _collect_one_node rep = collect_one_node(node) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/runner.py", line 565, in collect_one_node rep: CollectReport = ihook.pytest_make_collect_report(collector=collector) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_hooks.py", line 501, in __call__ return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_manager.py", line 119, in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pluggy/_callers.py", line 102, in _multicall res = hook_impl.function(*args) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/runner.py", line 390, in pytest_make_collect_report call = CallInfo.from_call(collect, "collect") File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/runner.py", line 340, in from_call result: Optional[TResult] = func() File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/runner.py", line 388, in collect return list(collector.collect()) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/python.py", line 576, in collect self._register_setup_module_fixture() File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/python.py", line 589, in _register_setup_module_fixture self.obj, ("setUpModule", "setup_module") File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/python.py", line 315, in obj self._obj = obj = self._getobj() File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/python.py", line 573, in _getobj return importtestmodule(self.path, self.config) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/python.py", line 520, in importtestmodule mod = import_path( File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/pathlib.py", line 584, in import_path importlib.import_module(module_name) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/importlib/__init__.py", line 90, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/_pytest/assertion/rewrite.py", line 178, in exec_module exec(co, module.__dict__) File "/home/runner/work/nonos/nonos/tests/test_plotting.py", line 4, in import numexpr as ne File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/numexpr/__init__.py", line 24, in from numexpr.interpreter import MAX_THREADS, use_vml, __BLOCK_SIZE1__ Traceback (most recent call last): File "/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/numpy/core/_multiarray_umath.py", line 44, in __getattr__ raise ImportError(msg) ImportError: A module that was compiled using NumPy 1.x cannot be run in NumPy 2.1.0.dev0 as it may crash. To support both 1.x and 2.x versions of NumPy, modules must be compiled with NumPy 2.0. Some module may need to rebuild instead e.g. with 'pybind11>=2.12'. If you are a user of the module, the easiest solution will be to downgrade to 'numpy<2' or try to upgrade the affected module. We expect that some modules will need time to support NumPy 2. ```

For more context:

It is recommended by NumPy developers to plan releases of packages with Python extensions a some point between 2.0.0rc1 and 2.0.0 (final), which from the looks of it should be a couple months from now.

neutrinoceros commented 8 months ago

I also updated the minimal requirement for numpy to sync it with python-requires: NumPy 1.19.3 was the first version to support Python 3.9, which is the current minimal requirement.

FrancescAlted commented 8 months ago

Your suggestion sounds good to me. However, there are still some issues in building the wheels in CI:

    numexpr/interpreter.cpp:1206:28: error: ‘PyArray_Descr’ {aka ‘struct _PyArray_Descr’} has no member named ‘elsize’
     1206 |                 dtypes[0]->elsize = (int)self->memsizes[1];
          |                            ^~~~~~
    numexpr/interpreter.cpp:1452:44: error: ‘PyArray_Descr’ {aka ‘struct _PyArray_Descr’} has no member named ‘elsize’
     1452 |         self->memsizes[i] = dtypes_tmp[i]->elsize;
          |                                            ^~~~~~
neutrinoceros commented 8 months ago

Oh, I didn't see this when I tested locally, but luckilly I already had to deal with this exact change in a different package downstream of NumPy ! I should be able to fix this quickly.

neutrinoceros commented 8 months ago

I was able to reproduce the crash locally when I switched from Python 3.12 to 3.9, and I just pushed a fix. For reference, this corresponds to an intentional change in NumPy 2.0's C API, xref https://github.com/numpy/numpy/pull/25943

FrancescAlted commented 8 months ago

LGTM. Thanks @neutrinoceros !

maxnoe commented 7 months ago

This was included in numexpr 2.10, right? However I see an import failure of numexpr claiming it wasn't compiled against numpy 2.0 here:

https://github.com/PyTables/PyTables/actions/runs/8734526298/job/23965425971?pr=1160#step:7:13

Any idea? Or am I misreadig this and it's not actually numexpr?

neutrinoceros commented 7 months ago

It was indeed included in 2.10 Grepping your example logs for numexpr, I see that version 2.8.7 is used

 Requirement already satisfied: numexpr>=2.6.2 in /usr/share/miniconda/envs/test/lib/python3.12/site-packages (from tables==3.9.3.dev0) (2.8.7)

so I think a different dependency may be adding constraints which prevent 2.10 from being installed.

I would recommend running pip list or some variation of it in CI between installation and tests, so it's easier to figure this stuff out :)

maxnoe commented 7 months ago

Thanks! I saw 2.10 but that was only in the isolated wheel build environment, indeed in the actual runtime environment, the old version was installed.