Skip to content

MAINT: Convert multiarray to multi-phase init (PEP 489) #29022

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

Merged
merged 1 commit into from
May 29, 2025

Conversation

AA-Turner
Copy link
Contributor

@AA-Turner AA-Turner commented May 21, 2025

@AA-Turner
Copy link
Contributor Author

The failures here are (will be) as the relevant parts of npy_static_pydata are not cleared on module deallocation, so e.g. npy_cpu_dispatch_tracer_init() and other functions checking the global state fail. We can add the m_clear/m_free slots to the moduledef, but I'm not sure which bits we should reset.

The other option is to keep treating those variables as global and instead re-add the same object to the new module. This may cause other problems though, as I don't believe it's the intended execution model.

There also appears to be a SIGSEV around the call to dtypemeta_wrap_legacy_descriptor() in arraytypes.c.src, but I'm not familiar enough with the templating process here.

cc @ngoldbaum if you'd be able to suggest anything?

A

@AA-Turner AA-Turner force-pushed the multi-phase/multiarray branch 2 times, most recently from 760440b to f5eed00 Compare May 22, 2025 05:47
@seberg
Copy link
Member

seberg commented May 22, 2025

Nice, will be great to make progress here!

We have a lot of global state, but much of it can probably just stay intact within the same interpreter (some cannot, e.g. because new singletons are created but this was always incorrect, so for a first start...).

So without multi-stage init, CPython would not run the module initially again if the so was already loaded (so deleting it from sys.modules and importing again doesn't trigger a second init)?

One problem with dtypemeta_wrap_legacy_descriptor is that it mutates global singleton instances. The actual problem should be in dtypemeta_wrap_legacy_descriptor in dtypemeta.c.

There is a check to catch types that are set to bad values, I honestly don't remember why it is there, and the loop definitely looks bad (it should just look up the type number of them descr and not loop!).
So it may just be replacing the loop with PyArray_DescrFromType(descr->type_num);.

So long we don't actually support subinterpreters, the old type object should actually be good to use. @AA-Turner maybe you can try applying something like this:

diff --git a/numpy/_core/src/multiarray/dtypemeta.c b/numpy/_core/src/multiarray/dtypemeta.c
index 0b1b0fb391..adb268097c 100644
--- a/numpy/_core/src/multiarray/dtypemeta.c
+++ b/numpy/_core/src/multiarray/dtypemeta.c
@@ -1075,6 +1075,11 @@ dtypemeta_wrap_legacy_descriptor(
     _PyArray_LegacyDescr *descr, PyArray_ArrFuncs *arr_funcs,
     PyTypeObject *dtype_super_class, const char *name, const char *alias)
 {
+    if (Py_TYPE(descr) != NULL && NPY_DTYPE(descr)->singleton == (PyArray_Descr *)descr) {
+        /* We seem to already have created a type, multiple inits? */
+        return 0;
+    }
+
     int has_type_set = Py_TYPE(descr) == &PyArrayDescr_Type;
 
     if (!has_type_set) {

(but there will likely be a long tail of these type of things, and of course the real fix will be to move the singletons into proper module state!)

@AA-Turner AA-Turner force-pushed the multi-phase/multiarray branch 2 times, most recently from 0683e1d to c7378be Compare May 28, 2025 14:43
@AA-Turner
Copy link
Contributor Author

Linux Qemu tests / riscv64 (pull_request) failure unrelated: E: Failed to fetch http://ports.ubuntu.com/ubuntu-ports/dists/jammy-updates/universe/binary-riscv64/Packages.gz File has unexpected size (1163769 != 1163765). Mirror sync in progress? [IP: 91.189.91.104 80]

@ngoldbaum
Copy link
Member

Yeah we get spurious failures due to network issues quite a bit, safe to ignore.

Also, wow, awesome, all the CI passes otherwise 🎉

@AA-Turner
Copy link
Contributor Author

Also, wow, awesome, all the CI passes otherwise 🎉

Before this is merged, one question. I've had to change test_reloading as multiarray is the first extension module imported, and hence we are now hitting the defensive error in

msg = f"""
IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!
Importing the numpy C-extensions failed. This error can happen for
many reasons, often due to issues with your setup or how NumPy was
installed.
{bad_c_module_info}
We have compiled some common reasons and troubleshooting tips at:
https://numpy.org/devdocs/user/troubleshooting-importerror.html
Please note and check the following:
* The Python version is: Python {major}.{minor} from "{sys.executable}"
* The NumPy version is: "{__version__}"
and make sure that they are the versions you expect.
Please carefully study the information and documentation linked above.
This is unlikely to be a NumPy issue but will be caused by a bad install
or environment on your machine.
Original error was: {exc}
"""

Is it worth adding a special case for where we detect re-initialisation? I've not done so currently because it is less work, but I'd be remiss for not asking.

A

@seberg
Copy link
Member

seberg commented May 28, 2025

Would be nice to avoid printing out that monster if it isn't too hard, considering that it is -- for once -- really unrelated.

@AA-Turner AA-Turner force-pushed the multi-phase/multiarray branch from c7378be to 67034c7 Compare May 28, 2025 16:45
@AA-Turner
Copy link
Contributor Author

I've updated _core/__init__.py to avoid raising the wall-of-text error for the reinitialisation exception, hopefully this works.

A

return 0;

err:
return -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big thing, but can you just remove the err label? No point in using goto err if there is no cleanup needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done; I avoided doing so in the first instance as it makes the diff much larger.

@AA-Turner AA-Turner force-pushed the multi-phase/multiarray branch from 67034c7 to d4b3f9b Compare May 29, 2025 09:04
@seberg seberg changed the title GH-29021: Convert multiarray to multi-phase init (PEP 489) MAINT: Convert multiarray to multi-phase init (PEP 489) May 29, 2025
@seberg seberg merged commit e107f24 into numpy:main May 29, 2025
74 checks passed
@AA-Turner AA-Turner deleted the multi-phase/multiarray branch May 29, 2025 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants