8000 Update signal from CPython 3.12.3 · RustPython/RustPython@e30e2c7 · GitHub
[go: up one dir, main page]

Skip to content

Commit e30e2c7

Browse files
committed
Update signal from CPython 3.12.3
1 parent 3d78ca8 commit e30e2c7

File tree

2 files changed

+122
-20
lines changed

2 files changed

+122
-20
lines changed

Lib/signal.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222

2323

2424
def _int_to_enum(value, enum_klass):
25-
"""Convert a numeric value to an IntEnum member.
26-
If it's not a known member, return the numeric value itself.
25+
"""Convert a possible numeric value to an IntEnum member.
26+
If it's not a known member, return the value itself.
2727
"""
28+
if not isinstance(value, int):
29+
return value
2830
try:
2931
return enum_klass(value)
3032
except ValueError:

Lib/test/test_signal.py

Lines changed: 118 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import enum
12
import errno
3+
import functools
24
import inspect
35
import os
46
import random
@@ -13,6 +15,7 @@
1315
from test import support
1416
from test.support import os_helper
1517
from test.support.script_helper import assert_python_ok, spawn_python
18+
from test.support import threading_helper
1619
try:
1720
import _testcapi
1821
except ImportError:
@@ -34,6 +37,32 @@ def test_enums(self):
3437
self.assertIsInstance(sig, signal.Signals)
3538
self.assertEqual(sys.platform, "win32")
3639

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=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+
3766
def test_functions_module_attr(self):
3867
# Issue #27718: If __all__ is not defined all non-builtin functions
3968
# should have correct __module__ to be displayed by pydoc.
@@ -48,6 +77,9 @@ class PosixTests(unittest.TestCase):
4877
def trivial_signal_handler(self, *args):
4978
pass
5079

80+
def create_handler_with_partial(self, argument):
81+
return functools.partial(self.trivial_signal_handler, argument)
82+
5183
# TODO: RUSTPYTHON
5284
@unittest.expectedFailure
5385
def test_out_of_range_signal_number_raises_error(self):
@@ -70,6 +102,28 @@ def test_getsignal(self):
70102
signal.signal(signal.SIGHUP, hup)
71103
self.assertEqual(signal.getsignal(signal.SIGHUP), hup)
72104

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+
73127
# TODO: RUSTPYTHON
74128
@unittest.expectedFailure
75129
def test_strsignal(self):
@@ -87,6 +141,10 @@ def test_interprocess_signal(self):
87141

88142
# TODO: RUSTPYTHON
89143
@unittest.expectedFailure
144+
@unittest.skipUnless(
145+
hasattr(signal, "valid_signals"),
146+
"requires signal.valid_signals"
147+
)
90148
def test_valid_signals(self):
91149
s = signal.valid_signals()
92150
self.assertIsInstance(s, set)
@@ -96,7 +154,21 @@ def test_valid_signals(self):
96154
self.assertNotIn(signal.NSIG, s)
97155
self.assertLess(len(s), signal.NSIG)
98156

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+
99170
@unittest.skipUnless(sys.executable, "sys.executable required.")
171+
@support.requires_subprocess()
100172
def test_keyboard_interrupt_exit_code(self):
101173
"""KeyboardInterrupt triggers exit via SIGINT."""
102174
process = subprocess.run(
@@ -153,6 +225,7 @@ def test_issue9324(self):
153225
# TODO: RUSTPYTHON
154226
@unittest.expectedFailure
155227
@unittest.skipUnless(sys.executable, "sys.executable required.")
228+
@support.requires_subprocess()
156229
def test_keyboard_interrupt_exit_code(self):
157230
"""KeyboardInterrupt triggers an exit using STATUS_CONTROL_C_EXIT."""
158231
# We don't test via os.kill(os.getpid(), signal.CTRL_C_EVENT) here
@@ -185,6 +258,7 @@ def test_invalid_fd(self):
185258
signal.set_wakeup_fd, fd)
186259

187260
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
261+
@unittest.skipUnless(support.has_socket_support, "needs working sockets.")
188262
def test_invalid_socket(self):
189263
sock = socket.socket()
190264
fd = sock.fileno()
@@ -193,6 +267,10 @@ def test_invalid_socket(self):
193267
signal.set_wakeup_fd, fd)
194268

195269
@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()")
196274
def test_set_wakeup_fd_result(self):
197275
r1, w1 = os.pipe()
198276
self.addCleanup(os.close, r1)
@@ -211,6 +289,8 @@ def test_set_wakeup_fd_result(self):
211289
self.assertEqual(signal.set_wakeup_fd(-1), -1)
212290

213291
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
292+
@unittest.skipIf(support.is_emscripten, "Emscripten cannot fstat pipes.")
293+
@unittest.skipUnless(support.has_socket_support, "needs working sockets.")
214294
def test_set_wakeup_fd_socket_result(self):
215295
sock1 = socket.socket()
216296
self.addCleanup(sock1.close)
@@ -230,6 +310,8 @@ def test_set_wakeup_fd_socket_result(self):
230310
# On Windows, files are always blocking and Windows does not provide a
231311
# function to test if a socket is in non-blocking mode.
232312
@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()")
233315
def test_set_wakeup_fd_blocking(self):
234316
rfd, wfd = os.pipe()
235317
self.addCleanup(os.close, rfd)
@@ -290,6 +372,7 @@ def check_signum(signals):
290372
assert_python_ok('-c', code)
291373

292374
@unittest.skipIf(_testcapi is None, 'need _testcapi')
375+
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
293376
def test_wakeup_write_error(self):
294377
# Issue #16105: write() errors in the C signal handler should not
295378
# pass silently.
@@ -628,6 +711,8 @@ def handler(signum, frame):
628711

629712
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
630713
@unittest.skipUnless(hasattr(signal, 'siginterrupt'), "needs signal.siginterrupt()")
714+
@support.requires_subprocess()
715+
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
631716
class SiginterruptTest(unittest.TestCase):
632717

633718
def readpipe_interrupted(self, interrupt):
@@ -708,6 +793,7 @@ def test_siginterrupt_on(self):
708793
interrupted = self.readpipe_interrupted(True)
709794
self.assertTrue(interrupted)
710795

796+
@support.requires_resource('walltime')
711797
def test_siginterrupt_off(self):
712798
# If a signal handler is installed and siginterrupt is called with
713799
# a false value for the second argument, when that signal arrives, it
@@ -781,15 +867,12 @@ def test_itimer_virtual(self):
781867
signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
782868
signal.setitimer(self.itimer, 0.3, 0.2)
783869

784-
start_time = time.monotonic()
785-
while time.monotonic() - start_time < 60.0:
870+
for _ in support.busy_retry(support.LONG_TIMEOUT):
786871
# use up some virtual time by doing real work
787872
_ = pow(12345, 67890, 10000019)
788873
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
793876

794877
# virtual itimer should be (0.0, 0.0) now
795878
self.assertEqual(signal.getitimer(self.itimer), (0.0, 0.0))
@@ -803,15 +886,12 @@ def test_itimer_prof(self):
803886
signal.signal(signal.SIGPROF, self.sig_prof)
804887
signal.setitimer(self.itimer, 0.2, 0.2)
805888

806-
start_time = time.monotonic()
807-
while time.monotonic() - start_time < 60.0:
889+
for _ in support.busy_retry(support.LONG_TIMEOUT):
808890
# do some work
809891
_ = pow(12345, 67890, 10000019)
810892
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
815895

816896
# profiling itimer should be (0.0, 0.0) now
817897
self.assertEqual(signal.getitimer(self.itimer), (0.0, 0.0))
@@ -873,6 +953,7 @@ def handler(signum, frame):
873953

874954
@unittest.skipUnless(hasattr(signal, 'pthread_kill'),
875955
'need signal.pthread_kill()')
956+
@threading_helper.requires_working_threading()
876957
def test_pthread_kill(self):
877958
code = """if 1:
878959
import signal
@@ -1009,6 +1090,7 @@ def test_sigtimedwait_negative_timeout(self):
10091090
'need signal.sigwait()')
10101091
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
10111092
'need signal.pthread_sigmask()')
1093+
@threading_helper.requires_working_threading()
10121094
def test_sigwait_thread(self):
10131095
# Check that calling sigwait() from a thread doesn't suspend the whole
10141096
# process. A new interpreter is spawned to avoid problems when mixing
@@ -1064,6 +1146,7 @@ def test_pthread_sigmask_valid_signals(self):
10641146

10651147
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
10661148
'need signal.pthread_sigmask()')
1149+
@threading_helper.requires_working_threading()
10671150
def test_pthread_sigmask(self):
10681151
code = """if 1:
10691152
import signal
@@ -1141,6 +1224,7 @@ def read_sigmask():
11411224

11421225
@unittest.skipUnless(hasattr(signal, 'pthread_kill'),
11431226
'need signal.pthread_kill()')
1227+
@threading_helper.requires_working_threading()
11441228
def test_pthread_kill_main_thread(self):
11451229
# Test that a signal can be sent to the main thread with pthread_kill()
11461230
# before any other thread has been created (see issue #12392).
@@ -1276,8 +1360,6 @@ def handler(signum, frame):
12761360
self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL
12771361

12781362
expected_sigs = 0
1279-
deadline = time.monotonic() + support.SHORT_TIMEOUT
1280-
12811363
while expected_sigs < N:
12821364
# Hopefully the SIGALRM will be received somewhere during
12831365
# initial processing of SIGUSR1.
@@ -1286,16 +1368,19 @@ def handler(signum, frame):
12861368

12871369
expected_sigs += 2
12881370
# 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
12911374

12921375
# All ITIMER_REAL signals should have been delivered to the
12931376
# Python handler
12941377
self.assertEqual(len(sigs), N, "Some signals were lost")
12951378

12961379
@unittest.skip("TODO: RUSTPYTHON; hang")
1380+
@unittest.skipIf(sys.platform == "darwin", "crashes due to system bug (FB13453490)")
12971381
@unittest.skipUnless(hasattr(signal, "SIGUSR1"),
12981382
"test needs SIGUSR1")
1383+
@threading_helper.requires_working_threading()
12991384
def test_stress_modifying_handlers(self):
13001385
# bpo-43406: race condition between trip_signal() and signal.signal
13011386
signum = signal.SIGUSR1
@@ -1314,7 +1399,7 @@ def set_interrupts():
13141399
num_sent_signals += 1
13151400

13161401
def cycle_handlers():
1317-
while num_sent_signals < 100:
1402+
while num_sent_signals < 100 or num_received_signals < 1:
13181403
for i in range(20000):
13191404
# Cycle between a Python-defined and a non-Python handler
13201405
for handler in [custom_handler, signal.SIG_IGN]:
@@ -1347,7 +1432,7 @@ def cycle_handlers():
13471432
if not ignored:
13481433
# Sanity check that some signals were received, but not all
13491434
self.assertGreater(num_received_signals, 0)
1350-
self.assertLess(num_received_signals, num_sent_signals)
1435+
self.assertLessEqual(num_received_signals, num_sent_signals)
13511436
finally:
13521437
do_stop = True
13531438
t.join()
@@ -1388,6 +1473,21 @@ def handler(a, b):
13881473
signal.raise_signal(signal.SIGINT)
13891474
self.assertTrue(is_ok)
13901475

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+
13911491

13921492
class PidfdSignalTest(unittest.TestCase):
13931493

0 commit comments

Comments
 (0)
0