1
+ import enum
1
2
import errno
3
+ import functools
2
4
import inspect
3
5
import os
4
6
import random
13
15
from test import support
14
16
from test .support import os_helper
15
17
from test .support .script_helper import assert_python_ok , spawn_python
18
+ from test .support import threading_helper
16
19
try :
17
20
import _testcapi
18
21
except ImportError :
@@ -34,6 +37,32 @@ def test_enums(self):
34
37
self .assertIsInstance (sig , signal .Signals )
35
38
self .assertEqual (sys .platform , "win32" )
36
39
40
+ CheckedSignals = enum ._old_convert_ (
41
+ enum .IntEnum , 'Signals' , 'signal' ,
42
+ lambda name :
43
+ name .isupper ()
44
+ and (name .startswith ('SIG' ) and not name .startswith ('SIG_' ))
45
+ or name .startswith ('CTRL_' ),
46
+ source = signal ,
47
+ )
48
+ enum ._test_simple_enum (CheckedSignals , signal .Signals )
49
+
50
+ CheckedHandlers = enum ._old_convert_ (
51
+ enum .IntEnum , 'Handlers' , 'signal' ,
52
+ lambda name : name in ('SIG_DFL' , 'SIG_IGN' ),
53
+ source =
628C
span>signal ,
54
+ )
55
+ enum ._test_simple_enum (CheckedHandlers , signal .Handlers )
56
+
57
+ Sigmasks = getattr (signal , 'Sigmasks' , None )
58
+ if Sigmasks is not None :
59
+ CheckedSigmasks = enum ._old_convert_ (
60
+ enum .IntEnum , 'Sigmasks' , 'signal' ,
61
+ lambda name : name in ('SIG_BLOCK' , 'SIG_UNBLOCK' , 'SIG_SETMASK' ),
62
+ source = signal ,
63
+ )
64
+ enum ._test_simple_enum (CheckedSigmasks , Sigmasks )
65
+
37
66
def test_functions_module_attr (self ):
38
67
# Issue #27718: If __all__ is not defined all non-builtin functions
39
68
# should have correct __module__ to be displayed by pydoc.
@@ -48,6 +77,9 @@ class PosixTests(unittest.TestCase):
48
77
def trivial_signal_handler (self , * args ):
49
78
pass
50
79
80
+ def create_handler_with_partial (self , argument ):
81
+ return functools .partial (self .trivial_signal_handler , argument )
82
+
51
83
# TODO: RUSTPYTHON
52
84
@unittest .expectedFailure
53
85
def test_out_of_range_signal_number_raises_error (self ):
@@ -70,6 +102,28 @@ def test_getsignal(self):
70
102
signal .signal (signal .SIGHUP , hup )
71
103
self .assertEqual (signal .getsignal (signal .SIGHUP ), hup )
72
104
105
+ def test_no_repr_is_called_on_signal_handler (self ):
106
+ # See https://github.com/python/cpython/issues/112559.
107
+
108
+ class MyArgument :
109
+ def __init__ (self ):
110
+ self .repr_count = 0
111
+
112
+ def __repr__ (self ):
113
+ self .repr_count += 1
114
+ return super ().__repr__ ()
115
+
116
+ argument = MyArgument ()
117
+ self .assertEqual (0 , argument .repr_count )
118
+
119
+ handler = self .create_handler_with_partial (argument )
120
+ hup = signal .signal (signal .SIGHUP , handler )
121
+ self .assertIsInstance (hup , signal .Handlers )
122
+ self .assertEqual (signal .getsignal (signal .SIGHUP ), handler )
123
+ signal .signal (signal .SIGHUP , hup )
124
+ self .assertEqual (signal .getsignal (signal .SIGHUP ), hup )
125
+ self .assertEqual (0 , argument .repr_count )
126
+
73
127
# TODO: RUSTPYTHON
74
128
@unittest .expectedFailure
75
129
def test_strsignal (self ):
@@ -87,6 +141,10 @@ def test_interprocess_signal(self):
87
141
88
142
# TODO: RUSTPYTHON
89
143
@unittest .expectedFailure
144
+ @unittest .skipUnless (
145
+ hasattr (signal , "valid_signals" ),
146
+ "requires signal.valid_signals"
147
+ )
90
148
def test_valid_signals (self ):
91
149
s = signal .valid_signals ()
92
150
self .assertIsInstance (s , set )
@@ -96,7 +154,21 @@ def test_valid_signals(self):
96
154
self .assertNotIn (signal .NSIG , s )
97
155
self .assertLess (len (s ), signal .NSIG )
98
156
157
+ # gh-91145: Make sure that all SIGxxx constants exposed by the Python
158
+ # signal module have a number in the [0; signal.NSIG-1] range.
159
+ for name in dir (signal ):
160
+ if not name .startswith ("SIG" ):
161
+ continue
162
+ if name in {"SIG_IGN" , "SIG_DFL" }:
163
+ # SIG_IGN and SIG_DFL are pointers
164
+ continue
165
+ with self .subTest (name = name ):
166
+ signum = getattr (signal , name )
167
+ self .assertGreaterEqual (signum , 0 )
168
+ self .assertLess (signum , signal .NSIG )
169
+
99
170
@unittest .skipUnless (sys .executable , "sys.executable required." )
171
+ @support .requires_subprocess ()
100
172
def test_keyboard_interrupt_exit_code (self ):
101
173
"""KeyboardInterrupt triggers exit via SIGINT."""
102
174
process = subprocess .run (
@@ -153,6 +225,7 @@ def test_issue9324(self):
153
225
# TODO: RUSTPYTHON
154
226
@unittest .expectedFailure
155
227
@unittest .skipUnless (sys .executable , "sys.executable required." )
228
+ @support .requires_subprocess ()
156
229
def test_keyboard_interrupt_exit_code (self ):
157
230
"""KeyboardInterrupt triggers an exit using STATUS_CONTROL_C_EXIT."""
158
231
# We don't test via os.kill(os.getpid(), signal.CTRL_C_EVENT) here
@@ -185,6 +258,7 @@ def test_invalid_fd(self):
185
258
signal .set_wakeup_fd , fd )
186
259
187
260
@unittest .expectedFailureIfWindows ("TODO: RUSTPYTHON" )
261
+ @unittest .skipUnless (support .has_socket_support , "needs working sockets." )
188
262
def test_invalid_socket (self ):
189
263
sock = socket .socket ()
190
264
fd = sock .fileno ()
@@ -193,6 +267,10 @@ def test_invalid_socket(self):
193
267
signal .set_wakeup_fd , fd )
194
268
195
269
@unittest .expectedFailureIfWindows ("TODO: RUSTPYTHON" )
270
+ # Emscripten does not support fstat on pipes yet.
271
+ # https://github.com/emscripten-core/emscripten/issues/16414
272
+ @unittest .skipIf (support .is_emscripten , "Emscripten cannot fstat pipes." )
273
+ @unittest .skipUnless (hasattr (os , "pipe" ), "requires os.pipe()" )
196
274
def test_set_wakeup_fd_result (self ):
197
275
r1 , w1 = os .pipe ()
198
276
self .addCleanup (os .close , r1 )
@@ -211,6 +289,8 @@ def test_set_wakeup_fd_result(self):
211
289
self .assertEqual (signal .set_wakeup_fd (- 1 ), - 1 )
212
290
213
291
@unittest .expectedFailureIfWindows ("TODO: RUSTPYTHON" )
292
+ @unittest .skipIf (support .is_emscripten , "Emscripten cannot fstat pipes." )
293
+ @unittest .skipUnless (support .has_socket_support , "needs working sockets." )
214
294
def test_set_wakeup_fd_socket_result (self ):
215
295
sock1 = socket .socket ()
216
296
self .addCleanup (sock1 .close )
@@ -230,6 +310,8 @@ def test_set_wakeup_fd_socket_result(self):
230
310
# On Windows, files are always blocking and Windows does not provide a
231
311
# function to test if a socket is in non-blocking mode.
232
312
@unittest .skipIf (sys .platform == "win32" , "tests specific to POSIX" )
313
+ @unittest .skipIf (support .is_emscripten , "Emscripten cannot fstat pipes." )
314
+ @unittest .skipUnless (hasattr (os , "pipe" ), "requires os.pipe()" )
233
315
def test_set_wakeup_fd_blocking (self ):
234
316
rfd , wfd = os .pipe ()
235
317
self .addCleanup (os .close , rfd )
@@ -290,6 +372,7 @@ def check_signum(signals):
290
372
assert_python_ok ('-c' , code )
291
373
292
374
@unittest .skipIf (_testcapi is None , 'need _testcapi' )
375
+ @unittest .skipUnless (hasattr (os , "pipe" ), "requires os.pipe()" )
293
376
def test_wakeup_write_error (self ):
294
377
# Issue #16105: write() errors in the C signal handler should not
295
378
# pass silently.
@@ -628,6 +711,8 @@ def handler(signum, frame):
628
711
629
712
@unittest .skipIf (sys .platform == "win32" , "Not valid on Windows" )
630
713
@unittest .skipUnless (hasattr (signal , 'siginterrupt' ), "needs signal.siginterrupt()" )
714
+ @support .requires_subprocess ()
715
+ @unittest .skipUnless (hasattr (os , "pipe" ), "requires os.pipe()" )
631
716
class SiginterruptTest (unittest .TestCase ):
632
717
633
718
def readpipe_interrupted (self , interrupt ):
@@ -708,6 +793,7 @@ def test_siginterrupt_on(self):
708
793
interrupted = self .readpipe_interrupted (True )
709
794
self .assertTrue (interrupted )
710
795
796
+ @support .requires_resource ('walltime' )
711
797
def test_siginterrupt_off (self ):
712
798
# If a signal handler is installed and siginterrupt is called with
713
799
# a false value for the second argument, when that signal arrives, it
@@ -781,15 +867,12 @@ def test_itimer_virtual(self):
781
867
signal .signal (signal .SIGVTALRM , self .sig_vtalrm )
782
868
signal .setitimer (self .itimer , 0.3 , 0.2 )
783
869
784
- start_time = time .monotonic ()
785
- while time .monotonic () - start_time < 60.0 :
870
+ for _ in support .busy_retry (support .LONG_TIMEOUT ):
786
871
# use up some virtual time by doing real work
787
872
_ = pow (12345 , 67890 , 10000019 )
788
873
if signal .getitimer (self .itimer ) == (0.0 , 0.0 ):
789
- break # sig_vtalrm handler stopped this itimer
790
- else : # Issue 8424
791
- self .skipTest ("timeout: likely cause: machine too slow or load too "
792
- "high" )
874
+ # sig_vtalrm handler stopped this itimer
875
+ break
793
876
794
877
# virtual itimer should be (0.0, 0.0) now
795
878
self .assertEqual (signal .getitimer (self .itimer ), (0.0 , 0.0 ))
@@ -803,15 +886,12 @@ def test_itimer_prof(self):
803
886
signal .signal (signal .SIGPROF , self .sig_prof )
804
887
signal .setitimer (self .itimer , 0.2 , 0.2 )
805
888
806
- start_time = time .monotonic ()
807
- while time .monotonic () - start_time < 60.0 :
889
+ for _ in support .busy_retry (support .LONG_TIMEOUT ):
808
890
# do some work
809
891
_ = pow (12345 , 67890 , 10000019 )
810
892
if signal .getitimer (self .itimer ) == (0.0 , 0.0 ):
811
- break # sig_prof handler stopped this itimer
812
- else : # Issue 8424
813
- self .skipTest ("timeout: likely cause: machine too slow or load too "
814
- "high" )
893
+ # sig_prof handler stopped this itimer
894
+ break
815
895
816
896
# profiling itimer should be (0.0, 0.0) now
817
897
self .assertEqual (signal .getitimer (self .itimer ), (0.0 , 0.0 ))
@@ -873,6 +953,7 @@ def handler(signum, frame):
873
953
874
954
@unittest .skipUnless (hasattr (signal , 'pthread_kill' ),
875
955
'need signal.pthread_kill()' )
956
+ @threading_helper .requires_working_threading ()
876
957
def test_pthread_kill (self ):
877
958
code = """if 1:
878
959
import signal
@@ -1009,6 +1090,7 @@ def test_sigtimedwait_negative_timeout(self):
1009
1090
'need signal.sigwait()' )
1010
1091
@unittest .skipUnless (hasattr (signal , 'pthread_sigmask' ),
1011
1092
'need signal.pthread_sigmask()' )
1093
+ @threading_helper .requires_working_threading ()
1012
1094
def test_sigwait_thread (self ):
1013
1095
# Check that calling sigwait() from a thread doesn't suspend the whole
1014
1096
# process. A new interpreter is spawned to avoid problems when mixing
@@ -1064,6 +1146,7 @@ def test_pthread_sigmask_valid_signals(self):
1064
1146
1065
1147
@unittest .skipUnless (hasattr (signal , 'pthread_sigmask' ),
1066
1148
'need signal.pthread_sigmask()' )
1149
+ @threading_helper .requires_working_threading ()
1067
1150
def test_pthread_sigmask (self ):
1068
1151
code = """if 1:
1069
1152
import signal
@@ -1141,6 +1224,7 @@ def read_sigmask():
1141
1224
1142
1225
@unittest .skipUnless (hasattr (signal , 'pthread_kill' ),
1143
1226
'need signal.pthread_kill()' )
1227
+ @threading_helper .requires_working_threading ()
1144
1228
def test_pthread_kill_main_thread (self ):
1145
1229
# Test that a signal can be sent to the main thread with pthread_kill()
1146
1230
# before any other thread has been created (see issue #12392).
@@ -1276,8 +1360,6 @@ def handler(signum, frame):
1276
1360
self .setsig (signal .SIGALRM , handler ) # for ITIMER_REAL
1277
1361
1278
1362
expected_sigs = 0
1279
- deadline = time .monotonic () + support .SHORT_TIMEOUT
1280
-
1281
1363
while expected_sigs < N :
1282
1364
# Hopefully the SIGALRM will be received somewhere during
1283
1365
# initial processing of SIGUSR1.
@@ -1286,16 +1368,19 @@ def handler(signum, frame):
1286
1368
1287
1369
expected_sigs += 2
1288
1370
# Wait for handlers to run to avoid signal coalescing
1289
- while len (sigs ) < expected_sigs and time .monotonic () < deadline :
1290
- time .sleep (1e-5 )
1371
+ for _ in support .sleeping_retry (support .SHORT_TIMEOUT ):
1372
+ if len (sigs ) >= expected_sigs :
1373
+ break
1291
1374
1292
1375
# All ITIMER_REAL signals should have been delivered to the
1293
1376
# Python handler
1294
1377
self .assertEqual (len (sigs ), N , "Some signals were lost" )
1295
1378
1296
1379
@unittest .skip ("TODO: RUSTPYTHON; hang" )
1380
+ @unittest .skipIf (sys .platform == "darwin" , "crashes due to system bug (FB13453490)" )
1297
1381
@unittest .skipUnless (hasattr (signal , "SIGUSR1" ),
1298
1382
"test needs SIGUSR1" )
1383
+ @threading_helper .requires_working_threading ()
1299
1384
def test_stress_modifying_handlers (self ):
1300
1385
# bpo-43406: race condition between trip_signal() and signal.signal
1301
1386
signum = signal .SIGUSR1
@@ -1314,7 +1399,7 @@ def set_interrupts():
1314
1399
num_sent_signals += 1
1315
1400
1316
1401
def cycle_handlers ():
1317
- while num_sent_signals < 100 :
1402
+ while num_sent_signals < 100 or num_received_signals < 1 :
1318
1403
for i in range (20000 ):
1319
1404
# Cycle between a Python-defined and a non-Python handler
1320
1405
for handler in [custom_handler , signal .SIG_IGN ]:
@@ -1347,7 +1432,7 @@ def cycle_handlers():
1347
1432
if not ignored :
1348
1433
# Sanity check that some signals were received, but not all
1349
1434
self .assertGreater (num_received_signals , 0 )
1350
- self .assertLess (num_received_signals , num_sent_signals )
1435
+ self .assertLessEqual (num_received_signals , num_sent_signals )
1351
1436
finally :
1352
1437
do_stop = True
1353
1438
t .join ()
@@ -1388,6 +1473,21 @@ def handler(a, b):
1388
1473
signal .raise_signal (signal .SIGINT )
1389
1474
self .assertTrue (is_ok )
1390
1475
1476
+ def test__thread_interrupt_main (self ):
1477
+ # See https://github.com/python/cpython/issues/102397
1478
+ code = """if 1:
1479
+ import _thread
1480
+ class Foo():
1481
+ def __del__(self):
1482
+ _thread.interrupt_main()
1483
+
1484
+ x = Foo()
1485
+ """
1486
+
1487
+ rc , out , err = assert_python_ok ('-c' , code )
1488
+ self .assertIn (b'OSError: Signal 2 ignored due to race condition' , err )
1489
+
1490
+
1391
1491
1392
1492
class PidfdSignalTest (unittest .TestCase ):
1393
1493
0 commit comments