Skip to content

Conversation

HaoZeke
Copy link
Contributor

@HaoZeke HaoZeke commented Apr 24, 2025

Closes #198. Basically:

  • Uses a critical section module scope for global cache variables in Cython
    • With the slow-and-fast path structuring recommended in the ft-guide
  • Uses C++ threading primitives (through Cython wrappers) for managing lazy imports
  • Signals to Cython that the code is indeed safe for ft builds
  • Splits the global sections out into their own files since earlier cython versions cannot handle critical_section Uses conditional compilation via IFnow.

Draft until I get the CI right for

  • testing and
  • building wheels.

The testing bit is mostly taken from this recent NumPy PR, but since there aren't any tests which use threading (nor AFAIK should there be, the NDIndex objects seem pretty immutable), might not even need the TSAN run.

@HaoZeke HaoZeke force-pushed the ftBuild branch 2 times, most recently from a02d49b to 18a050e Compare April 24, 2025 23:14
@HaoZeke
Copy link
Contributor Author

HaoZeke commented Apr 24, 2025

Leaving the thread sanitizer race warnings here for posterity..

Races
==================
WARNING: ThreadSanitizer: data race (pid=933)
  Read of size 8 at 0x7f0890502298 by main thread:
    #0 PyUnstable_InterpreterFrame_GetLine /tmp/python-build.20250221213836.92/Python-3.13.2/Python/frame.c:152:16 (libpython3.13t.so.1.0+0x3769a8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #1 PyFrame_GetLineNumber /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/frameobject.c:901:12 (libpython3.13t.so.1.0+0x16368d) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #2 frame_getlineno /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/frameobject.c:907:18 (libpython3.13t.so.1.0+0x16368d)
    #3 getset_get /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/descrobject.c:193:16 (libpython3.13t.so.1.0+0x136dc8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #4 _PyObject_GenericGetAttrWithDict /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:1665:19 (libpython3.13t.so.1.0+0x1cd525) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #5 PyObject_GenericGetAttr /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:1747:12 (libpython3.13t.so.1.0+0x1cd3[72](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:73)) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #6 PyObject_GetAttr /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:1261:18 (libpython3.13t.so.1.0+0x1cbc97) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #7 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:3766:28 (libpython3.13t.so.1.0+0x3225d3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #8 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x152aa8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #9 gen_send_ex2 /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:229:24 (libpython3.13t.so.1.0+0x152aa8)
    #10 gen_iternext /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:589:9 (libpython3.13t.so.1.0+0x150299) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #11 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:2786:24 (libpython3.13t.so.1.0+0x31f0ef) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #12 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x152aa8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #13 gen_send_ex2 /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:229:24 (libpython3.13t.so.1.0+0x152aa8)
    #14 gen_iternext /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:589:9 (libpython3.13t.so.1.0+0x150299) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #15 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:2786:24 (libpython3.13t.so.1.0+0x31f0ef) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #16 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #17 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
    #18 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #19 _PyVectorcall_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:2[73](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:74):16 (libpython3.13t.so.1.0+0x1227c3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #20 _PyObject_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:348:16 (libpython3.13t.so.1.0+0x1227c3)
    #21 _PyErr_CheckSignalsTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Modules/signalmodule.c:1855:22 (libpython3.13t.so.1.0+0x42dbe3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #22 handle_signals /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval_gil.c:828:9 (libpython3.13t.so.1.0+0x385f87) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #23 _PyEval_MakePendingCalls /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval_gil.c:1045:15 (libpython3.13t.so.1.0+0x385f87)
    #24 Py_MakePendingCalls /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval_gil.c:1073:12 (libpython3.13t.so.1.0+0x385f87)
    #25 ThreadHandle_join /tmp/python-build.20250221213836.92/Python-3.13.2/./Modules/_threadmodule.c:515:17 (libpython3.13t.so.1.0+0x499198) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #26 PyThreadHandleObject_join /tmp/python-build.20250221213836.92/Python-3.13.2/./Modules/_threadmodule.c:643:9 (libpython3.13t.so.1.0+0x4999b3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #37 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:166:16 (libpython3.13t.so.1.0+0x12258a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #38 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x12258a)
    #39 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:1502:19 (libpython3.13t.so.1.0+0x31a98b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #40 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #41 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
    #42 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #43 _PyObject_VectorcallDictTstate /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:146:15 (libpython3.13t.so.1.0+0x121680) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #44 _PyObject_Call_Prepend /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:504:24 (libpython3.13t.so.1.0+0x1231a6) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #45 slot_tp_call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/typeobject.c:9539:15 (libpython3.13t.so.1.0+0x22f76a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #46 _PyObject_MakeTpCall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:242:18 (libpython3.13t.so.1.0+0x12192e) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #47 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:166:16 (libpython3.13t.so.1.0+0x12258a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #48 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x12258a)
    #49 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:1502:19 (libpython3.13t.so.1.0+0x31a98b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #50 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #51 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
    #52 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #53 _PyObject_VectorcallDictTstate /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:146:15 (libpython3.13t.so.1.0+0x121680) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #54 _PyObject_Call_Prepend /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:504:24 (libpython3.13t.so.1.0+0x1231a6) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #55 slot_tp_call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/typeobject.c:9539:15 (libpython3.13t.so.1.0+0x22f76a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #56 _PyObject_MakeTpCall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:242:18 (libpython3.13t.so.1.0+0x12192e) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #57 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:166:16 (libpython3.13t.so.1.0+0x12258a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #58 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x12258a)
    #59 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:1502:19 (libpython3.13t.so.1.0+0x31a98b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #60 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314284) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #61 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314284)
    #62 PyEval_EvalCode /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:602:21 (libpython3.13t.so.1.0+0x314284)
    #63 builtin_exec_impl /tmp/python-build.20250221213836.92/Python-3.13.2/Python/bltinmodule.c:1145:17 (libpython3.13t.so.1.0+0x30eb96) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #64 builtin_exec /tmp/python-build.20250221213836.92/Python-3.13.2/Python/clinic/bltinmodule.c.h:556:20 (libpython3.13t.so.1.0+0x30eb96)
    #65 cfunction_vectorcall_FASTCALL_KEYWORDS /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/methodobject.c:441:24 (libpython3.13t.so.1.0+0x1c1853) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #66 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:168:11 (libpython3.13t.so.1.0+0x1224cc) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #67 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x1224cc)
    #68 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:813:23 (libpython3.13t.so.1.0+0x317d5b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #69 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #70 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
    #71 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #72 _PyVectorcall_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:273:16 (libpython3.13t.so.1.0+0x1227c3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #73 _PyObject_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:348:16 (libpython3.13t.so.1.0+0x1227c3)
    #[74](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:75) PyObject_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:373:12 (libpython3.13t.so.1.0+0x122847) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #75 pymain_run_module /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:349:14 (libpython3.13t.so.1.0+0x40be9a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #76 pymain_run_python /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:691:21 (libpython3.13t.so.1.0+0x40b5bb) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #77 Py_RunMain /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:7[76](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:77):5 (libpython3.13t.so.1.0+0x40b5bb)
    #78 pymain_main /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:806:12 (libpython3.13t.so.1.0+0x40bc68) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #79 Py_BytesMain /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:830:12 (libpython3.13t.so.1.0+0x40bcec) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
    #80 main /tmp/python-build.20250221213836.92/Python-3.13.2/./Programs/python.c:15:12 (python3.13+0xe8c9b) (BuildId: 3cc7488ffc292c8dd5400212ea3a107a04537e72)
SUMMARY: ThreadSanitizer: data race /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:2465:23 in new_reference
==================

@HaoZeke
Copy link
Contributor Author

HaoZeke commented Apr 25, 2025

Rather baffling doc building error, also in CircleCI:

Doc build errors
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:176: ERROR: Error in "plot" directive:
unknown option: "output-base-name".

.. plot::
   :context: reset
   :include-source: True
   :output-base-name: plot-{counter}
   :alt: A plot of 4*x*np.sin(x) - x**2/4 - 2*x from -10 to 10. The curve crosses the x-axis several times at irregular intervals.
   :caption: Plot of :math:`y = 4x\sin(x) - \frac{x^2}{4} - 2x`

   >>> import matplotlib.pyplot as plt
   >>> x = np.linspace(-10, 10, 10000) # 10000 evenly spaced points between -10 and 10
   >>> y = 4*x*np.sin(x) - x**2/4 - 2*x # our function
   >>> plt.scatter(x, y, marker=',', s=1)
   <matplotlib.collections.PathCollection object at ...> [docutils]
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:194: ERROR: Error in "plot" directive:
unknown option: "output-base-name".

.. plot::
   :context: close-figs
   :include-source: True
   :output-base-name: plot-{counter}
   :alt: A plot of only the parts of 4*x*np.sin(x) - x**2/4 - 2*x that are above the x-axis.
   :caption: Plot of :math:`y = 4x\sin(x) - \frac{x^2}{4} - 2x` where :math:`y > 0`

   >>> plt.scatter(x[y > 0], y[y > 0], marker=',', s=1)
   <matplotlib.collections.PathCollection object at ...> [docutils]
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:387: ERROR: Error in "plot" directive:
unknown option: "output-base-name".

.. plot::
   :context: reset
   :include-source: True
   :output-base-name: astronaut-{counter}
   :alt: An image of an astronaut, which is represented as a shape (512, 512, 3) array.

   >>> def imshow(image, title):
   ...     import matplotlib.pyplot as plt
   ...     plt.axis('off')
   ...     plt.title(title)
   ...     plt.imshow(image)
   >>> from skimage.data import astronaut
   >>> image = astronaut()
   >>> image.shape
   (512, 512, 3)
   >>> imshow(image, "Original Image") [docutils]
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:[41](https://github.com/Quansight-Labs/ndindex/actions/runs/14654158449/job/41126132114?pr=199#step:6:42)0: ERROR: Error in "plot" directive:
unknown option: "output-base-name".

@HaoZeke
Copy link
Contributor Author

HaoZeke commented Apr 25, 2025

Test failures are actually coverage issues so far (TSAN aside).

@asmeurer
Copy link
Contributor

The docs build error is because the docs require matplotlib/matplotlib#28187. I thought I had the CI set up to use that PR when building.

@asmeurer
Copy link
Contributor

the NDIndex objects seem pretty immutable

NDIndex objects are all designed to be 100% immutable. Even the objects that wrap arrays (which aren't yet Cythonized) copy them and set them to be read-only so that they can be immutable and hashable.

@asmeurer
Copy link
Contributor

Oh, regarding the docs thing, I guess I changed the option name in the matplotlib PR and never updated it here.

@ngoldbaum
Copy link
Contributor

I'm not sure whether the TSAN tests are hanging or if they're just really slow. I would avoid TSAN if it adds a ton of overhead to the tests.

ASAN will likely be a lot faster, although it won't catch as many things. It will catch serious issues, though.

@asmeurer
Copy link
Contributor

The docs build should be fixed on main.

@HaoZeke HaoZeke force-pushed the ftBuild branch 9 times, most recently from 27f43fa to 9d74e74 Compare May 2, 2025 12:03

# Slow path: Use critical section for initialization
# Use the dedicated module-level lock object
with cython.critical_section(_numpy_init_lock_obj):
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be better to use e.g. std::call_once from the C++ standard library. There are wrappers for the C and C++ standard library threading primitives shipped by Cython. C++ standard library threads have wider support so they're usually the best bet.

Critical sections are suspended when you call into the CPython C API (e.g. with the getattr or 'numpy' in sys.modules below), so this critical section isn't actually protecting the race you're trying to protect against from occurring. You could also use a PyMutex in the same way you're using a critical section, also no need to allocate a dummy object. PyMutex has the nice feature that it's impossible to deadlock with PyMutex on the GIL-enabled build.

With both options you might need to worry about the Python code executed by the getattr or sys.modules accessed somehow reentering this code. I don't think that's possible, so deadlocks shouldn't be possible either.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another example where I did something similar is the runtime import cache in NumPy: https://github.com/numpy/numpy/blob/aa7c4f5d59ee2b478eae9c7fa8759d59a8365c2d/numpy/_core/src/common/npy_import.h#L86-L109. Note the need for atomic loads to ensure that the single-initialization is thread-safe.

@rgommers
Copy link
Member

rgommers commented May 2, 2025

A sanity check for the CI approach here: I did not expect a TSan job to be added, or ASan. We haven't done that in any project except for NumPy (and maybe PyO3?). It's probably only going to catch things in other projects (CPython, NumPy, Cython) and be worth way more trouble than it's worth - all I'd expect is future maintenance headaches. The way I'd deal with this is to run it once in a Docker container, which is quick and easy with https://github.com/nascheme/cpython_sanity, and if it turns up nothing then check the box that it was clean in a comment or the PR description here.

The standard approach to testing is to add a single cp313t job with pytest-run-parallel, which is a lot easier to do and maintain.

@HaoZeke HaoZeke force-pushed the ftBuild branch 3 times, most recently from 11670aa to fdf069b Compare May 5, 2025 00:02
@rgommers
Copy link
Member

Half of this PR is now # pragma: no cover

It's happening on main as well (see gh-202), so it's unrelated to your changes it looks like. Adding a ton of pragma's can't be the right solution, it has to be some dependency that changed. Both hypothesis and Cython did releases on May 8th, so right before things started failing.

@asmeurer
Copy link
Contributor

We should replace 3.13-dev with just 3.13 in the CI (we could also add 3.14-dev, but that can be done in a separate PR).

@rgommers
Copy link
Member

We should replace 3.13-dev with just 3.13 in the CI (we could also add 3.14-dev, but that can be done in a separate PR).

I'll take this along too.

@rgommers
Copy link
Member

It's too early for 3.14-dev by the way: that attempts to build numpy, scikit-image and maybe more things from source.

@HaoZeke HaoZeke force-pushed the ftBuild branch 4 times, most recently from 474b0f5 to 3002c73 Compare May 13, 2025 17:55
HaoZeke added a commit to HaoZeke/ndindex that referenced this pull request May 13, 2025
@HaoZeke HaoZeke requested review from asmeurer and rgommers May 13, 2025 18:32
Copy link
Contributor

@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.

Much better now, just a few minor comments and suggestions inline

Copy link
Member

@rgommers rgommers left a comment

Choose a reason for hiding this comment

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

This looks good now, it's pretty clean. Thanks Rohit, Nathan, and Aaron!

@rgommers
Copy link
Member

Oh actually, it needs some history rewriting since there's no squash-merge enabled. Let me just do that right now and get this over the finish line.

HaoZeke and others added 4 commits May 14, 2025 12:29
Using a thin wrapper around `std::call_once`

Co-authored-by: ngoldbaum <[email protected]>
For PyPy 39
xref: Quansight-Labs#199 (comment)

Needed after switching Cython to C++ mode in `_tuple.pyx`

Co-authored-by: Ralf Gommers <[email protected]>
@rgommers rgommers merged commit 3162e53 into Quansight-Labs:main May 14, 2025
23 checks passed
@HaoZeke HaoZeke deleted the ftBuild branch May 14, 2025 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ENH: Support for free-threading
4 participants