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