-
-
Notifications
You must be signed in to change notification settings - Fork 215
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
Test numexpr against pytest-run-parallel on 3.13t #504
base: master
Are you sure you want to change the base?
Conversation
Thanks @andfoy. As it turns out, tests are not passing because pytest was not a dependency. Currently numexpr just uses But I like pytest, so if you want to add it to the list of dependencies, you are welcome. However, note that we allowed numexpr installations to run tests in the above way, without any additional dependency (but we can pass without that feature, I guess). |
Looks pretty good! @andfoy can you add a way for testing that the threaded version is passing? Or just adding Finally, @rgommers suggested saying something about the Thanks! |
.github/workflows/build.yml
Outdated
@@ -24,12 +24,13 @@ jobs: | |||
CIBW_BUILD: ${{ matrix.cibw_build }} | |||
CIBW_ARCHS_LINUX: ${{ matrix.arch }} | |||
CIBW_ARCHS_MACOS: "x86_64 arm64" | |||
CIBW_FREE_THREADED_SUPPORT: true |
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.
Note that this recently changed to CIBW_ENABLE
, so if its easy to bump cibuildwheel
to 2.22.0 then using that may be more future-proof: https://cibuildwheel.pypa.io/en/stable/changelog/#v2220
Nice, thanks for pushing on this @andfoy! It might be useful to split the whitespace cleanup off to another PR, since that'll reduce the diff of this PR by half - easier to then see what it actually took to add free-threading support. |
2ff0da0
to
6fe5a14
Compare
6fe5a14
to
61076a2
Compare
CI is broken because the upload action v4 refuses to upload multiple zip files named ![]() This should be easy to fix by using the |
Ok. I am trying to modernize the github build configuration on new PR #505 with @andfoy commits in. As soon as it turns green, I can proceed with the merge. BTW, do you think that you are finished with this one? On my side, I see it on a pretty good shape indeed. Thanks for your work so far! |
@andfoy I don't think you should spent more time fixing the CI builds, as this has been solved in #505, which should be merged soon. What I am missing is a benchmark showing the advantages of the new threaded model. numexpr is about performance, so most of the users will be curious on what advantages they can get, and how. At any rate, this can be added with another PR. |
@FrancescAlted just before merging, I need to mark the C modules as free-threaded safe |
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.
This looks pretty good to me. Testing with ThreadSanitizer (as documented at https://py-free-threading.github.io/debugging/#running-python-under-threadsanitizer) turns up a few failures related to the global_state
struct in numexpr/module.cpp|hpp
:
% CFLAGS="-fsanitize=thread -O2 -g" CXXFLAGS="-fsanitize=thread -O2 -g" python3.14t -m pip install -e . -v --no-build-isolation
% pytest .
=========================================================================== test session starts ===========================================================================
platform darwin -- Python 3.14.0a5+, pytest-8.3.4, pluggy-1.5.0
rootdir: /Users/rgommers/code/tmp/numexpr
configfile: pyproject.toml
plugins: hypothesis-6.125.3
collected 108 items
numexpr/tests/test_numexpr.py ..F....................F...................................................x.....................FF......... [100%]
================================================================================ FAILURES =================================================================================
_________________________________________________________________ test_numexpr.test_locals_clears_globals _________________________________________________________________
self = <numexpr.tests.test_numexpr.test_numexpr testMethod=test_locals_clears_globals>
@pytest.mark.thread_unsafe
def test_locals_clears_globals(self):
# Check for issue #313, whereby clearing f_locals also clear f_globals
# if in the top-frame. This cannot be done inside `unittest` as it is always
# executing code in a child frame.
script = r';'.join([
r"import numexpr as ne",
r"a=10",
r"ne.evaluate('1')",
r"a += 1",
r"ne.evaluate('2', local_dict={})",
r"a += 1",
r"ne.evaluate('3', global_dict={})",
r"a += 1",
r"ne.evaluate('4', local_dict={}, global_dict={})",
r"a += 1",
])
# Raises CalledProcessError on a non-normal exit
> check = subprocess.check_call([sys.executable, '-c', script])
numexpr/tests/test_numexpr.py:355:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
popenargs = (['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={});a += 1;ne.evaluate('3', global_dict={});a += 1;ne.evaluate('4', local_dict={}, global_dict={});a += 1"],)
kwargs = {}, retcode = -6
cmd = ['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={});a += 1;ne.evaluate('3', global_dict={});a += 1;ne.evaluate('4', local_dict={}, global_dict={});a += 1"]
def check_call(*popenargs, **kwargs):
"""Run command with arguments. Wait for command to complete. If
the exit code was zero then return, otherwise raise
CalledProcessError. The CalledProcessError object will have the
return code in the returncode attribute.
The arguments are the same as for the call function. Example:
check_call(["ls", "-l"])
"""
retcode = call(*popenargs, **kwargs)
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
> raise CalledProcessError(retcode, cmd)
E subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={});a += 1;ne.evaluate('3', global_dict={});a += 1;ne.evaluate('4', local_dict={}, global_dict={});a += 1"]' died with <Signals.SIGABRT: 6>.
../cpython/cpython-tsan/lib/python3.14t/subprocess.py:421: CalledProcessError
-------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
==================
WARNING: ThreadSanitizer: data race (pid=2918)
Write of size 4 at 0x000149d945e0 by thread T2:
#0 th_worker(void*) <null>:1244664592 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Previous write of size 4 at 0x000149d945e0 by thread T1:
#0 th_worker(void*) <null>:1244664592 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Location is global 'gs' at 0x000149d945b8 (interpreter.cpython-314t-darwin.so+0x345e0)
Thread T2 (tid=5968240, running) created by main thread at:
#0 pthread_create <null>:73400608 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1244659952 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1244659952 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1244659952 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
Thread T1 (tid=5968239, running) created by main thread at:
#0 pthread_create <null>:73400416 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1244664592 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1244664592 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1244664592 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
SUMMARY: ThreadSanitizer: data race (interpreter.cpython-314t-darwin.so:arm64+0x2753c) in th_worker(void*)+0x78
==================
ThreadSanitizer: reported 1 warnings
________________________________________________________________ test_numexpr2.test_locals_clears_globals _________________________________________________________________
self = <numexpr.tests.test_numexpr.test_numexpr2 testMethod=test_locals_clears_globals>
@pytest.mark.thread_unsafe
def test_locals_clears_globals(self):
# Check for issue #313, whereby clearing f_locals also clear f_globals
# if in the top-frame. This cannot be done inside `unittest` as it is always
# executing code in a child frame.
script = r';'.join([
r"import numexpr as ne",
r"a=10",
r"ne.evaluate('1')",
r"a += 1",
r"ne.evaluate('2', local_dict={})",
r"a += 1",
r"ne.evaluate('3', global_dict={})",
r"a += 1",
r"ne.evaluate('4', local_dict={}, global_dict={})",
r"a += 1",
])
# Raises CalledProcessError on a non-normal exit
> check = subprocess.check_call([sys.executable, '-c', script])
numexpr/tests/test_numexpr.py:355:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
popenargs = (['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={});a += 1;ne.evaluate('3', global_dict={});a += 1;ne.evaluate('4', local_dict={}, global_dict={});a += 1"],)
kwargs = {}, retcode = -6
cmd = ['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={});a += 1;ne.evaluate('3', global_dict={});a += 1;ne.evaluate('4', local_dict={}, global_dict={});a += 1"]
def check_call(*popenargs, **kwargs):
"""Run command with arguments. Wait for command to complete. If
the exit code was zero then return, otherwise raise
CalledProcessError. The CalledProcessError object will have the
return code in the returncode attribute.
The arguments are the same as for the call function. Example:
check_call(["ls", "-l"])
"""
retcode = call(*popenargs, **kwargs)
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
> raise CalledProcessError(retcode, cmd)
E subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={});a += 1;ne.evaluate('3', global_dict={});a += 1;ne.evaluate('4', local_dict={}, global_dict={});a += 1"]' died with <Signals.SIGABRT: 6>.
../cpython/cpython-tsan/lib/python3.14t/subprocess.py:421: CalledProcessError
-------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
==================
WARNING: ThreadSanitizer: data race (pid=2942)
Write of size 4 at 0x00014d0d45e0 by thread T2:
#0 th_worker(void*) <null>:1298141968 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Previous write of size 4 at 0x00014d0d45e0 by thread T1:
#0 th_worker(void*) <null>:1298141968 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Location is global 'gs' at 0x00014d0d45b8 (interpreter.cpython-314t-darwin.so+0x345e0)
Thread T2 (tid=5968414, running) created by main thread at:
#0 pthread_create <null>:126877984 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1298137328 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1298137328 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1298137328 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
Thread T1 (tid=5968413, running) created by main thread at:
#0 pthread_create <null>:126877792 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1298141968 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1298141968 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1298141968 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
SUMMARY: ThreadSanitizer: data race (interpreter.cpython-314t-darwin.so:arm64+0x2753c) in th_worker(void*)+0x78
==================
ThreadSanitizer: reported 1 warnings
_______________________________________________________________ test_threading_config.test_max_threads_set ________________________________________________________________
self = <numexpr.tests.test_numexpr.test_threading_config testMethod=test_max_threads_set>
def test_max_threads_set(self):
# Has to be done in a subprocess as `importlib.reload` doesn't let us
# re-initialize the threadpool
script = '\n'.join([
"import os",
"os.environ['NUMEXPR_MAX_THREADS'] = '4'",
"import numexpr",
"assert(numexpr.MAX_THREADS == 4)",
"exit(0)"])
> subprocess.check_call([sys.executable, '-c', script])
numexpr/tests/test_numexpr.py:1170:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
popenargs = (['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nos.environ['NUMEXPR_MAX_THREADS'] = '4'\nimport numexpr\nassert(numexpr.MAX_THREADS == 4)\nexit(0)"],)
kwargs = {}, retcode = -6
cmd = ['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nos.environ['NUMEXPR_MAX_THREADS'] = '4'\nimport numexpr\nassert(numexpr.MAX_THREADS == 4)\nexit(0)"]
def check_call(*popenargs, **kwargs):
"""Run command with arguments. Wait for command to complete. If
the exit code was zero then return, otherwise raise
CalledProcessError. The CalledProcessError object will have the
return code in the returncode attribute.
The arguments are the same as for the call function. Example:
check_call(["ls", "-l"])
"""
retcode = call(*popenargs, **kwargs)
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
> raise CalledProcessError(retcode, cmd)
E subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nos.environ['NUMEXPR_MAX_THREADS'] = '4'\nimport numexpr\nassert(numexpr.MAX_THREADS == 4)\nexit(0)"]' died with <Signals.SIGABRT: 6>.
../cpython/cpython-tsan/lib/python3.14t/subprocess.py:421: CalledProcessError
-------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
==================
WARNING: ThreadSanitizer: data race (pid=2953)
Write of size 4 at 0x000149e945e0 by thread T4:
#0 th_worker(void*) <null>:1245713168 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Previous write of size 4 at 0x000149e945e0 by thread T3:
#0 th_worker(void*) <null>:1245713168 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Location is global 'gs' at 0x000149e945b8 (interpreter.cpython-314t-darwin.so+0x345e0)
Thread T4 (tid=5968833, running) created by main thread at:
#0 pthread_create <null>:73400608 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1245708528 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1245708528 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1245708528 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
Thread T3 (tid=5968832, running) created by main thread at:
#0 pthread_create <null>:73400416 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1245713168 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1245713168 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1245713168 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
SUMMARY: ThreadSanitizer: data race (interpreter.cpython-314t-darwin.so:arm64+0x2753c) in th_worker(void*)+0x78
==================
ThreadSanitizer: reported 1 warnings
______________________________________________________________ test_threading_config.test_max_threads_unset _______________________________________________________________
self = <numexpr.tests.test_numexpr.test_threading_config testMethod=test_max_threads_unset>
def test_max_threads_unset(self):
# Has to be done in a subprocess as `importlib.reload` doesn't let us
# re-initialize the threadpool
script = '\n'.join([
"import os",
"if 'NUMEXPR_MAX_THREADS' in os.environ: os.environ.pop('NUMEXPR_MAX_THREADS')",
"if 'OMP_NUM_THREADS' in os.environ: os.environ.pop('OMP_NUM_THREADS')",
"import numexpr",
f"assert(numexpr.nthreads <= {MAX_THREADS})",
"exit(0)"])
> subprocess.check_call([sys.executable, '-c', script])
numexpr/tests/test_numexpr.py:1159:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
popenargs = (['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nif 'NUMEXPR_MAX_THREADS' in os.environ: os.environ.pop('NUM..._THREADS' in os.environ: os.environ.pop('OMP_NUM_THREADS')\nimport numexpr\nassert(numexpr.nthreads <= 16)\nexit(0)"],)
kwargs = {}, retcode = -6
cmd = ['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nif 'NUMEXPR_MAX_THREADS' in os.environ: os.environ.pop('NUME...UM_THREADS' in os.environ: os.environ.pop('OMP_NUM_THREADS')\nimport numexpr\nassert(numexpr.nthreads <= 16)\nexit(0)"]
def check_call(*popenargs, **kwargs):
"""Run command with arguments. Wait for command to complete. If
the exit code was zero then return, otherwise raise
CalledProcessError. The CalledProcessError object will have the
return code in the returncode attribute.
The arguments are the same as for the call function. Example:
check_call(["ls", "-l"])
"""
retcode = call(*popenargs, **kwargs)
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
> raise CalledProcessError(retcode, cmd)
E subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nif 'NUMEXPR_MAX_THREADS' in os.environ: os.environ.pop('NUMEXPR_MAX_THREADS')\nif 'OMP_NUM_THREADS' in os.environ: os.environ.pop('OMP_NUM_THREADS')\nimport numexpr\nassert(numexpr.nthreads <= 16)\nexit(0)"]' died with <Signals.SIGABRT: 6>.
../cpython/cpython-tsan/lib/python3.14t/subprocess.py:421: CalledProcessError
-------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
==================
WARNING: ThreadSanitizer: data race (pid=2961)
Write of size 4 at 0x00014b4945e0 by thread T2:
#0 th_worker(void*) <null>:1268781840 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Previous write of size 4 at 0x00014b4945e0 by thread T1:
#0 th_worker(void*) <null>:1268781840 (interpreter.cpython-314t-darwin.so:arm64+0x2753c)
Location is global 'gs' at 0x00014b4945b8 (interpreter.cpython-314t-darwin.so+0x345e0)
Thread T2 (tid=5968901, running) created by main thread at:
#0 pthread_create <null>:96469280 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1268777200 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1268777200 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1268777200 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
Thread T1 (tid=5968900, running) created by main thread at:
#0 pthread_create <null>:96469088 (libclang_rt.tsan_osx_dynamic.dylib:arm64+0x32790)
#1 init_threads() <null>:1268781840 (interpreter.cpython-314t-darwin.so:arm64+0x27b80)
#2 numexpr_set_nthreads(int) <null>:1268781840 (interpreter.cpython-314t-darwin.so:arm64+0x27f08)
#3 Py_set_num_threads(_object*, _object*) <null>:1268781840 (interpreter.cpython-314t-darwin.so:arm64+0x29fbc)
#4 cfunction_call methodobject.c:562 (python3.14t:arm64+0x10011109c)
#5 _PyObject_MakeTpCall call.c:242 (python3.14t:arm64+0x100077e30)
#6 PyObject_Vectorcall call.c:327 (python3.14t:arm64+0x100078a94)
#7 _PyEval_EvalFrameDefault generated_cases.c.h:1371 (python3.14t:arm64+0x100253314)
#8 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#9 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#10 builtin_exec bltinmodule.c.h:560 (python3.14t:arm64+0x1002497c4)
#11 cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:452 (python3.14t:arm64+0x100110358)
#12 _PyObject_Call call.c:348 (python3.14t:arm64+0x100078c4c)
#13 PyObject_Call call.c:373 (python3.14t:arm64+0x100078d00)
#14 _PyEval_EvalFrameDefault generated_cases.c.h:2421 (python3.14t:arm64+0x1002564a0)
#15 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#16 _PyFunction_Vectorcall call.c (python3.14t:arm64+0x100078f40)
#17 object_vacall call.c:819 (python3.14t:arm64+0x10007a828)
#18 PyObject_CallMethodObjArgs call.c:880 (python3.14t:arm64+0x10007a594)
#19 PyImport_ImportModuleLevelObject import.c:3764 (python3.14t:arm64+0x1002cc170)
#20 _PyEval_ImportName ceval.c:2670 (python3.14t:arm64+0x1002728f4)
#21 _PyEval_EvalFrameDefault generated_cases.c.h:5988 (python3.14t:arm64+0x10025f524)
#22 _PyEval_Vector ceval.c:1745 (python3.14t:arm64+0x10024f874)
#23 PyEval_EvalCode ceval.c:660 (python3.14t:arm64+0x10024f468)
#24 run_mod pythonrun.c:1389 (python3.14t:arm64+0x10030c8d0)
#25 _PyRun_SimpleStringFlagsWithName pythonrun.c:548 (python3.14t:arm64+0x100309920)
#26 Py_RunMain main.c:760 (python3.14t:arm64+0x100342238)
#27 pymain_main main.c:790 (python3.14t:arm64+0x100342724)
#28 Py_BytesMain main.c:814 (python3.14t:arm64+0x1003427d0)
#29 main python.c:15 (python3.14t:arm64+0x1000049a8)
SUMMARY: ThreadSanitizer: data race (interpreter.cpython-314t-darwin.so:arm64+0x2753c) in th_worker(void*)+0x78
========================================================================= short test summary info =========================================================================
FAILED numexpr/tests/test_numexpr.py::test_numexpr::test_locals_clears_globals - subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={...
FAILED numexpr/tests/test_numexpr.py::test_numexpr2::test_locals_clears_globals - subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import numexpr as ne;a=10;ne.evaluate('1');a += 1;ne.evaluate('2', local_dict={...
FAILED numexpr/tests/test_numexpr.py::test_threading_config::test_max_threads_set - subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nos.environ['NUMEXPR_MAX_THREADS'] = '4'\nimport numexpr\nassert(nume...
FAILED numexpr/tests/test_numexpr.py::test_threading_config::test_max_threads_unset - subprocess.CalledProcessError: Command '['/Users/rgommers/tsanvenv/bin/python', '-c', "import os\nif 'NUMEXPR_MAX_THREADS' in os.environ: os.environ.pop('NUMEXPR_MAX_...
========================================================== 4 failed, 103 passed, 1 xfailed, 9 warnings in 43.65s ==========================================================
ThreadSanitizer: reported 1 warnings
ThreadSanitizer: reported 1 warnings
The result with pytest .
and pytest . --parallel-threads=2
is the same four failures.
Sorry about the missing line numbers, that output would be easier to interpret with line numbers - I'm traveling and my macOS setup is a bit broken. Using Docker on Linux as in the link above should yield better results - maybe you want to try @andfoy? Anyway, it's these lines:
Lines 233 to 237 in 36aa11b
/* Now create the threads */ | |
for (tid = 0; tid < gs.nthreads; tid++) { | |
gs.tids[tid] = tid; | |
rc = pthread_create(&gs.threads[tid], NULL, th_worker, | |
(void *)&gs.tids[tid]); |
|
||
pytest = MagicMock() | ||
pytest.mark = MagicMock() | ||
pytest.mark.thread_unsafe = identity |
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.
It'd be better to register thread_unsafe
also if pytest
is installed but pytest-run-parallel
isn't. That way the test suite can be run with pytest
also without pytest-run-parallel
Given that |
Ok. If a micro-benchmark as such would not be useful, at least some small script using threading in Python code in combination with the new numexpr would be useful for users. If, for demonstration purposes, you want to add something that uses pandas, that would be fine too. |
Fixes #503
This PR checks that numexpr tests run in free-threaded Python 3.13t, both in non-concurrent as well as concurrent modes (via
pytest-run-parallel
). There were some trivial issues present in the library that caused major illegal memory accesses, which appeared due to changes on the memory allocation machinery in 3.13, those are fixed as part of this PR.