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

Skip to content

Commit 0273d78

Browse files
committed
Update signal from CPython 3.12.3
1 parent ed51d8d commit 0273d78

File tree

2 files changed

+126
-20
lines changed

2 files changed

+126
-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: 122 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:
@@ -21,6 +24,8 @@
2124

2225
class GenericTests(unittest.TestCase):
2326

27+
# TODO: RUSTPYTHON
28+
@unittest.expectedFailure
2429
def test_enums(self):
2530
for name in dir(signal):
2631
sig = getattr(signal, name)
@@ -34,6 +39,32 @@ def test_enums(self):
3439
self.assertIsInstance(sig, signal.Signals)
3540
self.assertEqual(sys.platform, "win32")
3641

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+
3768
def test_functions_module_attr(self):
3869
# Issue #27718: If __all__ is not defined all non-builtin functions
3970
# should have correct __module__ to be displayed by pydoc.
@@ -48,6 +79,9 @@ class PosixTests(unittest.TestCase):
4879
def trivial_signal_handler(self, *args):
4980
pass
5081

82+
def create_handler_with_partial(self, argument):
83+
return functools.partial(self.trivial_signal_handler, argument)
84+
5185
# TODO: RUSTPYTHON
5286
@unittest.expectedFailure
5387
def test_out_of_range_signal_number_raises_error(self):
@@ -70,6 +104,28 @@ def test_getsignal(self):
70104
signal.signal(signal.SIGHUP, hup)
71105
self.assertEqual(signal.getsignal(signal.SIGHUP), hup)
72106

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+
73129
# TODO: RUSTPYTHON
74130
@unittest.expectedFailure
75131
def test_strsignal(self):
@@ -87,6 +143,10 @@ def test_interprocess_signal(self):
87143

88144
# TODO: RUSTPYTHON
89145
@unittest.expectedFailure
146+
@unittest.skipUnless(
147+
hasattr(signal, "valid_signals"),
148+
"requires signal.valid_signals"
149+
)
90150
def test_valid_signals(self):
91151
s = signal.valid_signals()
92152
self.assertIsInstance(s, set)
@@ -96,7 +156,21 @@ def test_valid_signals(self):
96156
self.assertNotIn(signal.NSIG, s)
97157
self.assertLess(len(s), signal.NSIG)
98158

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+
99172
@unittest.skipUnless(sys.executable, "sys.executable required.")
173+
@support.requires_subprocess()
100174
def test_keyboard_interrupt_exit_code(self):
101175
"""KeyboardInterrupt triggers exit via SIGINT."""
102176
process = subprocess.run(
@@ -153,6 +227,7 @@ def test_issue9324(self):
153227
# TODO: RUSTPYTHON
154228
@unittest.expectedFailure
155229
@unittest.skipUnless(sys.executable, "sys.executable required.")
230+
@support.requires_subprocess()
156231
def test_keyboard_interrupt_exit_code(self):
157232
"""KeyboardInterrupt triggers an exit using STATUS_CONTROL_C_EXIT."""
158233
# We don't test via os.kill(os.getpid(), signal.CTRL_C_EVENT) here
@@ -185,6 +260,7 @@ def test_invalid_fd(self):
185260
signal.set_wakeup_fd, fd)
186261

187262
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
263+
@unittest.skipUnless(support.has_socket_support, "needs working sockets.")
188264
def test_invalid_socket(self):
189265
sock = socket.socket()
190266
fd = sock.fileno()
@@ -193,6 +269,10 @@ def test_invalid_socket(self):
193269
signal.set_wakeup_fd, fd)
194270

195271
@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()")
196276
def test_set_wakeup_fd_result(self):
197277
r1, w1 = os.pipe()
198278
self.addCleanup(os.close, r1)
@@ -211,6 +291,8 @@ def test_set_wakeup_fd_result(self):
211291
self.assertEqual(signal.set_wakeup_fd(-1), -1)
212292

213293
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
294+
@unittest.skipIf(support.is_emscripten, "Emscripten cannot fstat pipes.")
295+
@unittest.skipUnless(support.has_socket_support, "needs working sockets.")
214296
def test_set_wakeup_fd_socket_result(self):
215297
sock1 = socket.socket()
216298
self.addCleanup(sock1.close)
@@ -230,6 +312,8 @@ def test_set_wakeup_fd_socket_result(self):
230312
# On Windows, files are always blocking and Windows does not provide a
231313
# function to test if a socket is in non-blocking mode.
232314
@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()")
233317
def test_set_wakeup_fd_blocking(self):
234318
rfd, wfd = os.pipe()
235319
self.addCleanup(os.close, rfd)
@@ -290,6 +374,7 @@ def check_signum(signals):
290374
assert_python_ok('-c', code)
291375

292376
@unittest.skipIf(_testcapi is None, 'need _testcapi')
377+
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
293378
def test_wakeup_write_error(self):
294379
# Issue #16105: write() errors in the C signal handler should not
295380
# pass silently.
@@ -628,6 +713,8 @@ def handler(signum, frame):
628713

629714
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
630715
@unittest.skipUnless(hasattr(signal, 'siginterrupt'), "needs signal.siginterrupt()")
716+
@support.requires_subprocess()
717+
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
631718
class SiginterruptTest(unittest.TestCase):
632719

633720
def readpipe_interrupted(self, interrupt):
@@ -708,6 +795,7 @@ def test_siginterrupt_on(self):
708795
interrupted = self.readpipe_interrupted(True)
709796
self.assertTrue(interrupted)
710797

798+
@support.requires_resource('walltime')
711799
def test_siginterrupt_off(self):
712800
# If a signal handler is installed and siginterrupt is called with
713801
# a false value for the second argument, when that signal arrives, it
@@ -781,15 +869,12 @@ def test_itimer_virtual(self):
781869
signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
782870
signal.setitimer(self.itimer, 0.3, 0.2)
783871

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

794879
# virtual itimer should be (0.0, 0.0) now
795880
self.assertEqual(signal.getitimer(self.itimer), (0.0, 0.0))
@@ -803,15 +888,12 @@ def test_itimer_prof(self):
803888
signal.signal(signal.SIGPROF, self.sig_prof)
804889
signal.setitimer(self.itimer, 0.2, 0.2)
805890

806-
start_time = time.monotonic()
807-
while time.monotonic() - start_time < 60.0:
891+
for _ in support.busy_retry(support.LONG_TIMEOUT):
808892
# do some work
809893
_ = pow(12345, 67890, 10000019)
810894
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
815897

816898
# profiling itimer should be (0.0, 0.0) now
817899
self.assertEqual(signal.getitimer(self.itimer), (0.0, 0.0))
@@ -873,6 +955,7 @@ def handler(signum, frame):
873955

874956
@unittest.skipUnless(hasattr(signal, 'pthread_kill'),
875957
'need signal.pthread_kill()')
958+
@threading_helper.requires_working_threading()
876959
def test_pthread_kill(self):
877960
code = """if 1:
878961
import signal
@@ -1009,6 +1092,7 @@ def test_sigtimedwait_negative_timeout(self):
10091092
'need signal.sigwait()')
10101093
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
10111094
'need signal.pthread_sigmask()')
1095+
@threading_helper.requires_working_threading()
10121096
def test_sigwait_thread(self):
10131097
# Check that calling sigwait() from a thread doesn't suspend the whole
10141098
# process. A new interpreter is spawned to avoid problems when mixing
@@ -1064,6 +1148,7 @@ def test_pthread_sigmask_valid_signals(self):
10641148

10651149
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
10661150
'need signal.pthread_sigmask()')
1151+
@threading_helper.requires_working_threading()
10671152
def test_pthread_sigmask(self):
10681153
code = """if 1:
10691154
import signal
@@ -1141,6 +1226,7 @@ def read_sigmask():
11411226

11421227
@unittest.skipUnless(hasattr(signal, 'pthread_kill'),
11431228
'need signal.pthread_kill()')
1229+
@threading_helper.requires_working_threading()
11441230
def test_pthread_kill_main_thread(self):
11451231
# Test that a signal can be sent to the main thread with pthread_kill()
11461232
# before any other thread has been created (see issue #12392).
@@ -1276,8 +1362,6 @@ def handler(signum, frame):
12761362
self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL
12771363

12781364
expected_sigs = 0
1279-
deadline = time.monotonic() + support.SHORT_TIMEOUT
1280-
12811365
while expected_sigs < N:
12821366
# Hopefully the SIGALRM will be received somewhere during
12831367
# initial processing of SIGUSR1.
@@ -1286,16 +1370,19 @@ def handler(signum, frame):
12861370

12871371
expected_sigs += 2
12881372
# 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
12911376

12921377
# All ITIMER_REAL signals should have been delivered to the
12931378
# Python handler
12941379
self.assertEqual(len(sigs), N, "Some signals were lost")
12951380

12961381
@unittest.skip("TODO: RUSTPYTHON; hang")
1382+
@unittest.skipIf(sys.platform == "darwin", "crashes due to system bug (FB13453490)")
12971383
@unittest.skipUnless(hasattr(signal, "SIGUSR1"),
12981384
"test needs SIGUSR1")
1385+
@threading_helper.requires_working_threading()
12991386
def test_stress_modifying_handlers(self):
13001387
# bpo-43406: race condition between trip_signal() and signal.signal
13011388
signum = signal.SIGUSR1
@@ -1314,7 +1401,7 @@ def set_interrupts():
13141401
num_sent_signals += 1
13151402

13161403
def cycle_handlers():
1317-
while num_sent_signals < 100:
1404+
while num_sent_signals < 100 or num_received_signals < 1:
13181405
for i in range(20000):
13191406
# Cycle between a Python-defined and a non-Python handler
13201407
for handler in [custom_handler, signal.SIG_IGN]:
@@ -1347,7 +1434,7 @@ def cycle_handlers():
13471434
if not ignored:
13481435
# Sanity check that some signals were received, but not all
13491436
self.assertGreater(num_received_signals, 0)
1350-
self.assertLess(num_received_signals, num_sent_signals)
1437+
self.assertLessEqual(num_received_signals, num_sent_signals)
13511438
finally:
13521439
do_stop = True
13531440
t.join()
@@ -1388,6 +1475,23 @@ def handler(a, b):
13881475
signal.raise_signal(signal.SIGINT)
13891476
self.assertTrue(is_ok)
13901477

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

13921496
class PidfdSignalTest(unittest.TestCase):
13931497

0 commit comments

Comments
 (0)
0