Skip to content

Commit 7adfa88

Browse files
jopemachineyouknowone
authored andcommitted
Update threading lib and tests from CPython 3.12.0a0 (RustPython#4156)
* Update threading.py from CPython v3.12.0a0 * Update test_threading.py from CPython v3.12.0a0 * mark failing tests --------- Co-authored-by: Jeong YunWon <jeong@youknowone.org>
1 parent 24adb64 commit 7adfa88

2 files changed

Lines changed: 117 additions & 27 deletions

File tree

Lib/test/test_threading.py

Lines changed: 106 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
import test.support
6-
from test.support import threading_helper
6+
from test.support import threading_helper, requires_subprocess
77
from test.support import verbose, cpython_only, os_helper
88
from test.support.import_helper import import_module
99
from test.support.script_helper import assert_python_ok, assert_python_failure
@@ -25,16 +25,14 @@
2525
from test import lock_tests
2626
from test import support
2727

28+
threading_helper.requires_working_threading(module=True)
2829

2930
# Between fork() and exec(), only async-safe functions are allowed (issues
3031
# #12316 and #11870), and fork() from a worker thread is known to trigger
3132
# problems with some operating systems (issue #3863): skip problematic tests
3233
# on platforms known to behave badly.
3334
platforms_to_skip = ('netbsd5', 'hp-ux11')
3435

35-
# Is Python built with Py_DEBUG macro defined?
36-
Py_DEBUG = hasattr(sys, 'gettotalrefcount')
37-
3836

3937
def restore_default_excepthook(testcase):
4038
testcase.addCleanup(setattr, threading, 'excepthook', threading.excepthook)
@@ -123,6 +121,33 @@ def func(): pass
123121
thread = threading.Thread(target=func)
124122
self.assertEqual(thread.name, "Thread-5 (func)")
125123

124+
def test_args_argument(self):
125+
# bpo-45735: Using list or tuple as *args* in constructor could
126+
# achieve the same effect.
127+
num_list = [1]
128+
num_tuple = (1,)
129+
130+
str_list = ["str"]
131+
str_tuple = ("str",)
132+
133+
list_in_tuple = ([1],)
134+
tuple_in_list = [(1,)]
135+
136+
test_cases = (
137+
(num_list, lambda arg: self.assertEqual(arg, 1)),
138+
(num_tuple, lambda arg: self.assertEqual(arg, 1)),
139+
(str_list, lambda arg: self.assertEqual(arg, "str")),
140+
(str_tuple, lambda arg: self.assertEqual(arg, "str")),
141+
(list_in_tuple, lambda arg: self.assertEqual(arg, [1])),
142+
(tuple_in_list, lambda arg: self.assertEqual(arg, (1,)))
143+
)
144+
145+
for args, target in test_cases:
146+
with self.subTest(target=target, args=args):
147+
t = threading.Thread(target=target, args=args)
148+
t.start()
149+
t.join()
150+
126151
@cpython_only
127152
def test_disallow_instantiation(self):
128153
# Ensure that the type disallows instantiation (bpo-43916)
@@ -509,8 +534,7 @@ def test_daemon_param(self):
509534
t = threading.Thread(daemon=True)
510535
self.assertTrue(t.daemon)
511536

512-
@unittest.skipUnless(hasattr(threading.Lock(), '_at_fork_reinit'), 'TODO: RUSTPYTHON, exit_handler needs lock._at_fork_reinit')
513-
@unittest.skipUnless(hasattr(os, 'fork'), 'needs os.fork()')
537+
@support.requires_fork()
514538
def test_fork_at_exit(self):
515539
# bpo-42350: Calling os.fork() after threading._shutdown() must
516540
# not log an error.
@@ -538,7 +562,7 @@ def exit_handler():
538562
self.assertEqual(out, b'')
539563
self.assertEqual(err.rstrip(), b'child process ok')
540564

541-
@unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
565+
@support.requires_fork()
542566
def test_dummy_thread_after_fork(self):
543567
# Issue #14308: a dummy thread in the active list doesn't mess up
544568
# the after-fork mechanism.
@@ -565,8 +589,7 @@ def background_thread(evt):
565589
self.assertEqual(out, b'')
566590
self.assertEqual(err, b'')
567591

568-
@unittest.skipUnless(hasattr(sys, 'getswitchinterval'), "TODO: RUSTPYTHON, needs sys.getswitchinterval()")
569-
@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
592+
@support.requires_fork()
570593
def test_is_alive_after_fork(self):
571594
# Try hard to trigger #18418: is_alive() could sometimes be True on
572595
# threads that vanished after a fork.
@@ -600,7 +623,7 @@ def f():
600623
th.start()
601624
th.join()
602625

603-
@unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
626+
@support.requires_fork()
604627
@unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
605628
def test_main_thread_after_fork(self):
606629
code = """if 1:
@@ -622,7 +645,7 @@ def test_main_thread_after_fork(self):
622645
self.assertEqual(data, "MainThread\nTrue\nTrue\n")
623646

624647
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
625-
@unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
648+
@support.requires_fork()
626649
@unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
627650
@unittest.skipIf(os.name != 'posix', "test needs POSIX semantics")
628651
def test_main_thread_after_fork_from_nonmain_thread(self):
@@ -837,6 +860,7 @@ def callback():
837860
callback()
838861
finally:
839862
sys.settrace(old_trace)
863+
threading.settrace(old_trace)
840864

841865
def test_gettrace(self):
842866
def noop_trace(frame, event, arg):
@@ -850,6 +874,37 @@ def noop_trace(frame, event, arg):
850874
finally:
851875
threading.settrace(old_trace)
852876

877+
# TODO: RUSTPYTHON
878+
@unittest.expectedFailure
879+
def test_gettrace_all_threads(self):
880+
def fn(*args): pass
881+
old_trace = threading.gettrace()
882+
first_check = threading.Event()
883+
second_check = threading.Event()
884+
885+
trace_funcs = []
886+
def checker():
887+
trace_funcs.append(sys.gettrace())
888+
first_check.set()
889+
second_check.wait()
890+
trace_funcs.append(sys.gettrace())
891+
892+
try:
893+
t = threading.Thread(target=checker)
894+
t.start()
895+
first_check.wait()
896+
threading.settrace_all_threads(fn)
897+
second_check.set()
898+
t.join()
899+
self.assertEqual(trace_funcs, [None, fn])
900+
self.assertEqual(threading.gettrace(), fn)
901+
self.assertEqual(sys.gettrace(), fn)
902+
finally:
903+
threading.settrace_all_threads(old_trace)
904+
905+
self.assertEqual(threading.gettrace(), old_trace)
906+
self.assertEqual(sys.gettrace(), old_trace)
907+
853908
def test_getprofile(self):
854909
def fn(*args): pass
855910
old_profile = threading.getprofile()
@@ -859,6 +914,37 @@ def fn(*args): pass
859914
finally:
860915
threading.setprofile(old_profile)
861916

917+
# TODO: RUSTPYTHON
918+
@unittest.expectedFailure
919+
def test_getprofile_all_threads(self):
920+
def fn(*args): pass
921+
old_profile = threading.getprofile()
922+
first_check = threading.Event()
923+
second_check = threading.Event()
924+
925+
profile_funcs = []
926+
def checker():
927+
profile_funcs.append(sys.getprofile())
928+
first_check.set()
929+
second_check.wait()
930+
profile_funcs.append(sys.getprofile())
931+
932+
try:
933+
t = threading.Thread(target=checker)
934+
t.start()
935+
first_check.wait()
936+
threading.setprofile_all_threads(fn)
937+
second_check.set()
938+
t.join()
939+
self.assertEqual(profile_funcs, [None, fn])
940+
self.assertEqual(threading.getprofile(), fn)
941+
self.assertEqual(sys.getprofile(), fn)
942+
finally:
943+
threading.setprofile_all_threads(old_profile)
944+
945+
self.assertEqual(threading.getprofile(), old_profile)
946+
self.assertEqual(sys.getprofile(), old_profile)
947+
862948
@cpython_only
863949
def test_shutdown_locks(self):
864950
for daemon in (False, True):
@@ -929,16 +1015,6 @@ def noop(): pass
9291015
threading.Thread(target=noop).start()
9301016
# Thread.join() is not called
9311017

932-
@unittest.skipUnless(Py_DEBUG, 'need debug build (Py_DEBUG)')
933-
def test_debug_deprecation(self):
934-
# bpo-44584: The PYTHONTHREADDEBUG environment variable is deprecated
935-
rc, out, err = assert_python_ok("-Wdefault", "-c", "pass",
936-
PYTHONTHREADDEBUG="1")
937-
msg = (b'DeprecationWarning: The threading debug '
938-
b'(PYTHONTHREADDEBUG environment variable) '
939-
b'is deprecated and will be removed in Python 3.12')
940-
self.assertIn(msg, err)
941-
9421018
def test_import_from_another_thread(self):
9431019
# bpo-1596321: If the threading module is first import from a thread
9441020
# different than the main thread, threading._shutdown() must handle
@@ -1006,7 +1082,7 @@ def test_1_join_on_shutdown(self):
10061082
"""
10071083
self._run_and_join(script)
10081084

1009-
@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
1085+
@support.requires_fork()
10101086
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
10111087
# TODO: RUSTPYTHON need to fix test_1_join_on_shutdown then this might work
10121088
@unittest.expectedFailure
@@ -1029,7 +1105,7 @@ def test_2_join_in_forked_process(self):
10291105
"""
10301106
self._run_and_join(script)
10311107

1032-
@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
1108+
@support.requires_fork()
10331109
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
10341110
@unittest.skip("TODO: RUSTPYTHON, flaky test")
10351111
def test_3_join_in_forked_from_thread(self):
@@ -1075,8 +1151,9 @@ def test_4_daemon_threads(self):
10751151
10761152
def random_io():
10771153
'''Loop for a while sleeping random tiny amounts and doing some I/O.'''
1154+
import test.test_threading as mod
10781155
while True:
1079-
with open(os.__file__, 'rb') as in_f:
1156+
with open(mod.__file__, 'rb') as in_f:
10801157
stuff = in_f.read(200)
10811158
with open(os.devnull, 'wb') as null_f:
10821159
null_f.write(stuff)
@@ -1100,7 +1177,7 @@ def main():
11001177
rc, out, err = assert_python_ok('-c', script)
11011178
self.assertFalse(err)
11021179

1103-
@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
1180+
@support.requires_fork()
11041181
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
11051182
def test_reinit_tls_after_fork(self):
11061183
# Issue #13817: fork() would deadlock in a multithreaded program with
@@ -1124,8 +1201,9 @@ def do_fork_and_wait():
11241201
for t in threads:
11251202
t.join()
11261203

1127-
@unittest.skipUnless(hasattr(sys, '_current_frames'), "TODO: RUSTPYTHON, needs sys._current_frames()")
1128-
@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
1204+
@support.requires_fork()
1205+
# TODO: RUSTPYTHON
1206+
@unittest.expectedFailure
11291207
def test_clear_threads_states_after_fork(self):
11301208
# Issue #17094: check that threads states are cleared after fork()
11311209

@@ -1280,6 +1358,7 @@ def test_releasing_unacquired_lock(self):
12801358
self.assertRaises(RuntimeError, lock.release)
12811359

12821360
@unittest.skip("TODO: RUSTPYTHON, flaky test")
1361+
@requires_subprocess()
12831362
def test_recursion_limit(self):
12841363
# Issue 9670
12851364
# test that excessive recursion within a non-main thread causes

Lib/threading.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@ def settrace(func):
7979
global _trace_hook
8080
_trace_hook = func
8181

82+
# TODO: RUSTPYTHON
83+
def settrace_all_threads(func):
84+
"""Set a trace function for all threads started from the threading module
85+
and all Python threads that are currently executing.
86+
87+
The func will be passed to sys.settrace() for each thread, before its run()
88+
method is called.
89+
"""
90+
settrace(func)
91+
_sys._settraceallthreads(func)
92+
8293
def gettrace():
8394
"""Get the trace function as set by threading.settrace()."""
8495
return _trace_hook

0 commit comments

Comments
 (0)