Skip to content

Conversation

@ikrommyd
Copy link
Contributor

@ikrommyd ikrommyd commented Jan 14, 2026

Closes #30519

I now get this locally which didn't match before

In [1]: import numpy as np
   ...:
   ...: array = np.array([1, 2, 3])
   ...:
   ...: match array:
   ...:   case [a, b, c]:
   ...:       print(f"Matched sequence: {a}, {b}, {c}")
   ...:   case _:
   ...:       print("No pattern matched")
   ...:
Matched sequence: 1, 2, 3

In [2]: import numpy as np
   ...:
   ...: array = np.ma.array([1, 2, 3])
   ...:
   ...: match array:
   ...:   case [a, b, c]:
   ...:       print(f"Matched sequence: {a}, {b}, {c}")
   ...:   case _:
   ...:       print("No pattern matched")
   ...:
Matched sequence: 1, 2, 3

I'm putting this up to see what CI says first

@ikrommyd
Copy link
Contributor Author

I have added testing for ndarray and masked array. Please let me know what more do you want tested here (other subclasses for example) and whether we can do testing against the wider ecosystem to ensure this doesn't break anything. I was lazy so I had claude write me this:

import numpy as np
import tempfile
import os

# 1. ndarray (base)
print("=== ndarray ===")
arr = np.array([1, 2, 3])
match arr:
  case [a, b, c]:
      print(f"PASS: matched {a}, {b}, {c}")
  case _:
      print("FAIL")

# 2. MaskedArray
print("\n=== MaskedArray ===")
arr = np.ma.array([1, 2, 3], mask=[0, 1, 0])
match arr:
  case [a, b, c]:
      print(f"PASS: matched {a}, {b is np.ma.masked}, {c}")
  case _:
      print("FAIL")

# 3. recarray
print("\n=== recarray ===")
dt = np.dtype([('name', 'U10'), ('age', 'i4')])
arr = np.array([('Alice', 25), ('Bob', 30)], dtype=dt).view(np.recarray)
match arr:
  case [first, second]:
      print(f"PASS: matched {first.name}={first.age}, {second.name}={second.age}")
  case _:
      print("FAIL")

# 4. matrix (deprecated)
print("\n=== matrix ===")
with np.testing.suppress_warnings() as sup:
  sup.filter(PendingDeprecationWarning)
  arr = np.matrix([[1, 2], [3, 4]])
match arr:
  case [row1, row2]:
      print(f"PASS: matched {row1.tolist()}, {row2.tolist()}")
  case _:
      print("FAIL")

# 5. chararray (legacy)
print("\n=== chararray ===")
arr = np.char.array(['hello', 'world', 'test'])
match arr:
  case [a, b, c]:
      print(f"PASS: matched '{a}', '{b}', '{c}'")
  case _:
      print("FAIL")

# 6. memmap
print("\n=== memmap ===")
with tempfile.NamedTemporaryFile(delete=False) as f:
  fname = f.name
arr = np.memmap(fname, dtype='float32', mode='w+', shape=(3,))
arr[:] = [1.0, 2.0, 3.0]
match arr:
  case [a, b, c]:
      print(f"PASS: matched {a}, {b}, {c}")
  case _:
      print("FAIL")
del arr
os.unlink(fname)

they all fail without this PR while the output with this PR is

=== ndarray ===
PASS: matched 1, 2, 3

=== MaskedArray ===
PASS: matched 1, True, 3

=== recarray ===
PASS: matched Alice=25, Bob=30

=== matrix ===
<ipython-input-1-bec69a8c025f>:35: DeprecationWarning: NumPy warning suppression and assertion utilities are deprecated. Use warnings.catch_warnings, warnings.filterwarnings, pytest.warns, or pytest.filterwarnings instead. (Deprecated NumPy 2.4)
  with np.testing.suppress_warnings() as sup:
PASS: matched [[1, 2]], [[3, 4]]

=== chararray ===
PASS: matched 'hello', 'world', 'test'

=== memmap ===
PASS: matched 1.0, 2.0, 3.0

@ikrommyd
Copy link
Contributor Author

ikrommyd commented Jan 15, 2026

There is a segfault in macos_arm64 - 3.14t parallel testing with trace below. It doesn't look related to this PR to me but I could be very wrong here. I just don't know Py_TPFLAGS_SEQUENCE being set for subclasses like (matrix) could cause this crash. I also can't reproduce locally on my macbook with the exact setup as the CI yaml and I've been running spin test -p 4 -- --timeout=600 --durations=10 for the past hour with no problems. I even downloaded 3.14t from https://github.com/actions/python-versions/releases/tag/3.14.2-20014991423 to have the same build of cpython as CI.

 Fatal Python error: Segmentation fault

<Cannot show all threads while the GIL is disabled>
Stack (most recent call first):
  File "/Users/runner/work/numpy/numpy/build-install/usr/lib/python3.14t/site-packages/numpy/matrixlib/defmatrix.py", line 971 in T
  File "/Users/runner/work/numpy/numpy/build-install/usr/lib/python3.14t/site-packages/numpy/matrixlib/tests/test_defmatrix.py", line 299 in test_instance_methods
  File "/Library/Frameworks/PythonT.framework/Versions/3.14/lib/python3.14t/site-packages/pytest_run_parallel/plugin.py", line 80 in closure
  File "/Library/Frameworks/PythonT.framework/Versions/3.14/lib/python3.14t/threading.py", line 1024 in run
  File "/Library/Frameworks/PythonT.framework/Versions/3.14/lib/python3.14t/threading.py", line 1082 in _bootstrap_inner
  File "/Library/Frameworks/PythonT.framework/Versions/3.14/lib/python3.14t/threading.py", line 1044 in _bootstrap

Current thread's C stack trace (most recent call first):
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _Py_DumpStack+0x48 [0x102056adc]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at faulthandler_dump_c_stack+0x58 [0x10206bacc]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at faulthandler_fatal_error+0x178 [0x10206b960]
  Binary file "/usr/lib/system/libsystem_platform.dylib", at _sigtramp+0x38 [0x182d1b584]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyObject_MiMalloc+0x18 [0x101e02124]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at PyType_GenericAlloc+0xa0 [0x101e23dc4]
  Binary file "/Users/runner/work/numpy/numpy/build-install/usr/lib/python3.14t/site-packages/numpy/_core/_multiarray_umath.cpython-314t-darwin.so", at PyArray_NewFromDescr_int+0x1e8 [0x10255fba0]
  Binary file "/Users/runner/work/numpy/numpy/build-install/usr/lib/python3.14t/site-packages/numpy/_core/_multiarray_umath.cpython-314t-darwin.so", at PyArray_NewFromDescrAndBase+0x1c [0x102560628]
  Binary file "/Users/runner/work/numpy/numpy/build-install/usr/lib/python3.14t/site-packages/numpy/_core/_multiarray_umath.cpython-314t-darwin.so", at PyArray_Transpose+0x1c0 [0x1025c6cb8]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at method_vectorcall_VARARGS+0x78 [0x101d6e320]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyEval_EvalFrameDefault+0x2b854 [0x101f15e40]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyFunction_Vectorcall+0x134 [0x101d59b4c]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at PyObject_CallOneArg+0x78 [0x101d597f8]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at PyObject_GenericGetAttr+0x228 [0x101debcb0]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at builtin_getattr+0x190 [0x101ee43bc]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyEval_EvalFrameDefault+0x1e9dc [0x101f08fc8]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyFunction_Vectorcall+0x134 [0x101d59b4c]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at method_vectorcall+0x140 [0x101d5d988]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyEval_EvalFrameDefault+0x2097c [0x101f0af68]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyFunction_Vectorcall+0x134 [0x101d59b4c]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at method_vectorcall+0x140 [0x101d5d988]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at context_run+0x84 [0x101fd26e8]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyEval_EvalFrameDefault+0x2a98c [0x101f14f78]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at _PyFunction_Vectorcall+0x134 [0x101d59b4c]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at method_vectorcall+0x140 [0x101d5d988]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at thread_run+0xc4 [0x1020dbd54]
  Binary file "/Library/Frameworks/PythonT.framework/Versions/3.14/PythonT", at pythread_wrapper+0x30 [0x102052ff0]
  Binary file "/usr/lib/system/libsystem_pthread.dylib", at _pthread_start+0x88 [0x182ceaf3c]
  Binary file "/usr/lib/system/libsystem_pthread.dylib", at thread_start+0x8 [0x182ce5d24]

@ikrommyd
Copy link
Contributor Author

Hmm unless the stack is wrong and the test that's actually crashing is numpy/ma/tests/test_subclassing.py ················ cause that's whats in the log right before the seg fault. This could be related to the flag in theory.

@ngoldbaum
Copy link
Member

I'm also not able to reproduce the segfault.

That said, I do see a data race inside CPython when I run test_subclassing.py under a TSan build:

SUMMARY: ThreadSanitizer: data race call.c:820 in object_vacall
Details
WARNING: ThreadSanitizer: data race (pid=15392)
  Read of size 8 at 0x00010b6de6f8 by thread T325:
    #0 object_vacall call.c:820 (libpython3.14t.dylib:arm64+0x8e0f4)
    #1 PyObject_CallFunctionObjArgs call.c:927 (libpython3.14t.dylib:arm64+0x8e49c)
    #2 array_str <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x254dd8)
    #3 PyObject_Str object.c:822 (libpython3.14t.dylib:arm64+0x13834c)
    #4 unicode_vectorcall unicodeobject.c:15640 (libpython3.14t.dylib:arm64+0x1ec760)
    #5 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8bf74)
    #6 _PyEval_EvalFrameDefault generated_cases.c.h:1619 (libpython3.14t.dylib:arm64+0x27c1cc)
    #7 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #8 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #9 vectorcall_method typeobject.c:2965 (libpython3.14t.dylib:arm64+0x196254)
    #10 slot_tp_str typeobject.c:10211 (libpython3.14t.dylib:arm64+0x1a496c)
    #11 PyObject_Str object.c:822 (libpython3.14t.dylib:arm64+0x13834c)
    #12 unicode_vectorcall unicodeobject.c:15640 (libpython3.14t.dylib:arm64+0x1ec760)
    #13 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8bf74)
    #14 _PyEval_EvalFrameDefault generated_cases.c.h:1619 (libpython3.14t.dylib:arm64+0x27c1cc)
    #15 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #16 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #17 method_vectorcall classobject.c:73 (libpython3.14t.dylib:arm64+0x909a4)
    #18 _PyObject_Call call.c:348 (libpython3.14t.dylib:arm64+0x8c228)
    #19 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #20 _PyEval_EvalFrameDefault generated_cases.c.h:2654 (libpython3.14t.dylib:arm64+0x27eed4)
    #21 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #22 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #23 method_vectorcall classobject.c:73 (libpython3.14t.dylib:arm64+0x909a4)
    #24 context_run context.c:722 (libpython3.14t.dylib:arm64+0x2c21f8)
    #25 _PyEval_EvalFrameDefault generated_cases.c.h:3744 (libpython3.14t.dylib:arm64+0x28225c)
    #26 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #27 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #28 method_vectorcall classobject.c:73 (libpython3.14t.dylib:arm64+0x909a4)
    #29 _PyObject_Call call.c:348 (libpython3.14t.dylib:arm64+0x8c228)
    #30 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #31 thread_run _threadmodule.c:359 (libpython3.14t.dylib:arm64+0x416930)
    #32 pythread_wrapper thread_pthread.h:242 (libpython3.14t.dylib:arm64+0x366d04)

  Previous write of size 8 at 0x00010b6de6f8 by thread T327:
    #0 partial_vectorcall_fallback _functoolsmodule.c:378 (libpython3.14t.dylib:arm64+0x424f94)
    #1 partial_vectorcall _functoolsmodule.c:393 (libpython3.14t.dylib:arm64+0x4248dc)
    #2 object_vacall call.c:820 (libpython3.14t.dylib:arm64+0x8e110)
    #3 PyObject_CallFunctionObjArgs call.c:927 (libpython3.14t.dylib:arm64+0x8e49c)
    #4 array_str <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x254dd8)
    #5 PyObject_Str object.c:822 (libpython3.14t.dylib:arm64+0x13834c)
    #6 unicode_vectorcall unicodeobject.c:15640 (libpython3.14t.dylib:arm64+0x1ec760)
    #7 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8bf74)
    #8 _PyEval_EvalFrameDefault generated_cases.c.h:1619 (libpython3.14t.dylib:arm64+0x27c1cc)
    #9 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #10 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #11 vectorcall_method typeobject.c:2965 (libpython3.14t.dylib:arm64+0x196254)
    #12 slot_tp_str typeobject.c:10211 (libpython3.14t.dylib:arm64+0x1a496c)
    #13 PyObject_Str object.c:822 (libpython3.14t.dylib:arm64+0x13834c)
    #14 unicode_vectorcall unicodeobject.c:15640 (libpython3.14t.dylib:arm64+0x1ec760)
    #15 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8bf74)
    #16 _PyEval_EvalFrameDefault generated_cases.c.h:1619 (libpython3.14t.dylib:arm64+0x27c1cc)
    #17 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #18 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #19 method_vectorcall classobject.c:73 (libpython3.14t.dylib:arm64+0x909a4)
    #20 _PyObject_Call call.c:348 (libpython3.14t.dylib:arm64+0x8c228)
    #21 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #22 _PyEval_EvalFrameDefault generated_cases.c.h:2654 (libpython3.14t.dylib:arm64+0x27eed4)
    #23 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #24 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #25 method_vectorcall classobject.c:73 (libpython3.14t.dylib:arm64+0x909a4)
    #26 context_run context.c:722 (libpython3.14t.dylib:arm64+0x2c21f8)
    #27 _PyEval_EvalFrameDefault generated_cases.c.h:3744 (libpython3.14t.dylib:arm64+0x28225c)
    #28 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #29 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #30 method_vectorcall classobject.c:73 (libpython3.14t.dylib:arm64+0x909a4)
    #31 _PyObject_Call call.c:348 (libpython3.14t.dylib:arm64+0x8c228)
    #32 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #33 thread_run _threadmodule.c:359 (libpython3.14t.dylib:arm64+0x416930)
    #34 pythread_wrapper thread_pthread.h:242 (libpython3.14t.dylib:arm64+0x366d04)

  Thread T325 (tid=110351450, running) created by main thread at:
    #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x30e28)
    #1 do_start_joinable_thread thread_pthread.h:289 (libpython3.14t.dylib:arm64+0x365964)
    #2 PyThread_start_joinable_thread thread_pthread.h:331 (libpython3.14t.dylib:arm64+0x3657a8)
    #3 do_start_new_thread _threadmodule.c:1868 (libpython3.14t.dylib:arm64+0x4165cc)
    #4 thread_PyThread_start_joinable_thread _threadmodule.c:1991 (libpython3.14t.dylib:arm64+0x415298)
    #5 cfunction_call methodobject.c:564 (libpython3.14t.dylib:arm64+0x130ad8)
    #6 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #7 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #8 _PyEval_EvalFrameDefault generated_cases.c.h:3227 (libpython3.14t.dylib:arm64+0x280b44)
    #9 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #10 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #11 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #12 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #13 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #14 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #15 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #16 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #17 _PyEval_EvalFrameDefault generated_cases.c.h:3227 (libpython3.14t.dylib:arm64+0x280b44)
    #18 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #19 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #20 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #21 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #22 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #23 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #24 _PyObject_Call call.c:361 (libpython3.14t.dylib:arm64+0x8c1e8)
    #25 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #26 _PyEval_EvalFrameDefault generated_cases.c.h:2654 (libpython3.14t.dylib:arm64+0x27eed4)
    #27 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #28 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #29 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #30 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #31 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #32 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #33 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #34 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #35 _PyEval_EvalFrameDefault generated_cases.c.h:3227 (libpython3.14t.dylib:arm64+0x280b44)
    #36 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #37 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #38 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #39 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #40 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #41 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #42 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #43 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #44 _PyEval_EvalFrameDefault generated_cases.c.h:2959 (libpython3.14t.dylib:arm64+0x27fb80)
    #45 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #46 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #47 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #48 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #49 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #50 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #51 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #52 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #53 _PyEval_EvalFrameDefault generated_cases.c.h:2959 (libpython3.14t.dylib:arm64+0x27fb80)
    #54 PyEval_EvalCode ceval.c:975 (libpython3.14t.dylib:arm64+0x277ac0)
    #55 builtin_exec bltinmodule.c.h:571 (libpython3.14t.dylib:arm64+0x2717a4)
    #56 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:465 (libpython3.14t.dylib:arm64+0x12fe90)
    #57 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8bf74)
    #58 _PyEval_EvalFrameDefault generated_cases.c.h:1619 (libpython3.14t.dylib:arm64+0x27c1cc)
    #59 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #60 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #61 _PyObject_Call call.c:348 (libpython3.14t.dylib:arm64+0x8c228)
    #62 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #63 pymain_run_module main.c:353 (libpython3.14t.dylib:arm64+0x380ad4)
    #64 Py_RunMain main.c:775 (libpython3.14t.dylib:arm64+0x3800c0)
    #65 pymain_main main.c:805 (libpython3.14t.dylib:arm64+0x380870)
    #66 Py_BytesMain main.c:829 (libpython3.14t.dylib:arm64+0x380970)
    #67 main <null> (python3.14:arm64+0x100000738)

  Thread T327 (tid=110351452, running) created by main thread at:
    #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x30e28)
    #1 do_start_joinable_thread thread_pthread.h:289 (libpython3.14t.dylib:arm64+0x365964)
    #2 PyThread_start_joinable_thread thread_pthread.h:331 (libpython3.14t.dylib:arm64+0x3657a8)
    #3 do_start_new_thread _threadmodule.c:1868 (libpython3.14t.dylib:arm64+0x4165cc)
    #4 thread_PyThread_start_joinable_thread _threadmodule.c:1991 (libpython3.14t.dylib:arm64+0x415298)
    #5 cfunction_call methodobject.c:564 (libpython3.14t.dylib:arm64+0x130ad8)
    #6 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #7 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #8 _PyEval_EvalFrameDefault generated_cases.c.h:3227 (libpython3.14t.dylib:arm64+0x280b44)
    #9 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #10 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #11 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #12 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #13 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #14 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #15 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #16 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #17 _PyEval_EvalFrameDefault generated_cases.c.h:3227 (libpython3.14t.dylib:arm64+0x280b44)
    #18 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #19 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #20 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #21 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #22 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #23 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #24 _PyObject_Call call.c:361 (libpython3.14t.dylib:arm64+0x8c1e8)
    #25 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #26 _PyEval_EvalFrameDefault generated_cases.c.h:2654 (libpython3.14t.dylib:arm64+0x27eed4)
    #27 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #28 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #29 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #30 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #31 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #32 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #33 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #34 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #35 _PyEval_EvalFrameDefault generated_cases.c.h:3227 (libpython3.14t.dylib:arm64+0x280b44)
    #36 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #37 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #38 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #39 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #40 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #41 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #42 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #43 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #44 _PyEval_EvalFrameDefault generated_cases.c.h:2959 (libpython3.14t.dylib:arm64+0x27fb80)
    #45 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #46 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #47 _PyObject_VectorcallDictTstate call.c:146 (libpython3.14t.dylib:arm64+0x8b1cc)
    #48 _PyObject_Call_Prepend call.c:504 (libpython3.14t.dylib:arm64+0x8cbb4)
    #49 call_method typeobject.c:2937 (libpython3.14t.dylib:arm64+0x19a3b8)
    #50 slot_tp_call typeobject.c:10254 (libpython3.14t.dylib:arm64+0x19a1cc)
    #51 _PyObject_MakeTpCall call.c:242 (libpython3.14t.dylib:arm64+0x8b46c)
    #52 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8c008)
    #53 _PyEval_EvalFrameDefault generated_cases.c.h:2959 (libpython3.14t.dylib:arm64+0x27fb80)
    #54 PyEval_EvalCode ceval.c:975 (libpython3.14t.dylib:arm64+0x277ac0)
    #55 builtin_exec bltinmodule.c.h:571 (libpython3.14t.dylib:arm64+0x2717a4)
    #56 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:465 (libpython3.14t.dylib:arm64+0x12fe90)
    #57 PyObject_Vectorcall call.c:327 (libpython3.14t.dylib:arm64+0x8bf74)
    #58 _PyEval_EvalFrameDefault generated_cases.c.h:1619 (libpython3.14t.dylib:arm64+0x27c1cc)
    #59 _PyEval_Vector ceval.c:2083 (libpython3.14t.dylib:arm64+0x277eac)
    #60 _PyFunction_Vectorcall call.c (libpython3.14t.dylib:arm64+0x8c5b8)
    #61 _PyObject_Call call.c:348 (libpython3.14t.dylib:arm64+0x8c228)
    #62 PyObject_Call call.c:373 (libpython3.14t.dylib:arm64+0x8c2a0)
    #63 pymain_run_module main.c:353 (libpython3.14t.dylib:arm64+0x380ad4)
    #64 Py_RunMain main.c:775 (libpython3.14t.dylib:arm64+0x3800c0)
    #65 pymain_main main.c:805 (libpython3.14t.dylib:arm64+0x380870)
    #66 Py_BytesMain main.c:829 (libpython3.14t.dylib:arm64+0x380970)
    #67 main <null> (python3.14:arm64+0x100000738)

SUMMARY: ThreadSanitizer: data race call.c:820 in object_vacall

but I also see that same data race on numpy main, so I don't think it has anything to do with this PR.

I'm going to push a commit so the test job prints more output. Let's see if it's just a random flake...

@ngoldbaum
Copy link
Member

Seems to be a flaky failure.

@ikrommyd
Copy link
Contributor Author

Yeah it passed this time. I've also been running while (spin test -p 4); do; done locally for the past 2 hours with no issues.

@ikrommyd
Copy link
Contributor Author

Alright so, I was considering adding pattern matching tests here for all the ndarray subclasses that numpy makes. Should I proceed with that here?

@colesbury
Copy link

I don't think the stacktrace is wrong, but it's very possible that the bug or memory corruption occurs earlier than the crash. The C stacktrace ends with _PyObject_MiMalloc(). Crashes there usually mean something like:

  • Something freed the same block of memory twice
  • Something overwrote freed memory and corrupted allocator data structures

The functools.partial data race is fixed in 3.15, but not in 3.14. The PR that fixed it was also complicated, which is why I think it wasn't backported. We should be able to fix it for 3.14.3 with a smaller PR.

@colesbury
Copy link

Also, I suspect that the data race seen by @ngoldbaum wasn't the cause of the crash, but it's hard to know for sure.

@ngoldbaum
Copy link
Member

Thanks for taking a look, Sam! I'll keep an eye out for this and perhaps try running with an ASan build to see if that spots anything.

Alright so, I was considering adding pattern matching tests here for all the ndarray subclasses that numpy makes. Should I proceed with that here?

Skip chararray, that's in the process of being deprecated: #30605. So I guess that just leaves memmap and recarray. Is there another one I'm missing?

@ikrommyd
Copy link
Contributor Author

matrix too right? Yeah that's all I saw with grepping for ndarray inheritance patterns. On it.

@ngoldbaum
Copy link
Member

ngoldbaum commented Jan 15, 2026

Ah yes, matrix too, but IMO that's skippable (see e.g. #13835 for why it's unloved). Even doing more than masked array is probably overkill.

@ikrommyd
Copy link
Contributor Author

More tests added. I sleep better if I test the other subclasses too. Matrix is “interesting” because it has peculiar pattern matching in the sense that it is always 2D. The test failure is unrelated (it failed during adding deadsnakes ppa)

Copy link
Member

@ngoldbaum ngoldbaum left a comment

Choose a reason for hiding this comment

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

Overall looks great.

I think we should probably add a release note for the new ability to do structural pattern matching perhaps including a code example. I don't know if it needs to be explicitly documented, but maybe look over the docs for a spot to add a short explanation of how users can pattern match with arrays, and then you can link to the new docs from the release note.

There's a README in the root of the doc/release folder that explains how to write release notes.

@ikrommyd
Copy link
Contributor Author

I have added release notes and docs (hopefully correctly). I thought that adding a small section at the bottom of this https://numpy.org/devdocs/reference/arrays.ndarray.html was a good place to do so. Let me know if you want it more visible like maybe in the user guide too.

@ikrommyd
Copy link
Contributor Author

Should I also revert Nathan's spin test verbosity change or do you wanna keep that for the future? (it may be useful although bit noisy in the log)

@ngoldbaum
Copy link
Member

Should I also revert Nathan's spin test verbosity change or do you wanna keep that for the future?

Let's keep it, it would have made debugging the crash you ran into a bit easier.

@ngoldbaum ngoldbaum added this to the 2.5.0 Release milestone Jan 16, 2026
Copy link
Member

@ngoldbaum ngoldbaum left a comment

Choose a reason for hiding this comment

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

Looks good to me! I have one small suggestion for the docs.

I'm going to hold off on merging this because it hasn't been open for very long and it's a new feature. I doubt anyone will object but I like to give time for for other maintainers to raise objections on ENH PRs.

Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com>
@ngoldbaum
Copy link
Member

No comments since last week: let's merge this. Thanks @ikrommyd!

@ngoldbaum ngoldbaum merged commit 91029c1 into numpy:main Jan 21, 2026
75 checks passed
@ikrommyd ikrommyd deleted the enable-tpflags-sequence branch January 21, 2026 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ENH: Set Py_TPFLAGS_SEQUENCE for ndarray

3 participants