Skip to content

Commit 2ac26e4

Browse files
authored
Implement signal constants on POSIX platforms (#1945)
1 parent 8122f8a commit 2ac26e4

File tree

4 files changed

+364
-89
lines changed

4 files changed

+364
-89
lines changed

eng/scripts/generate_os_codes.py

+30
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,34 @@ def generate_TIOC_commands(cw):
304304
generate_codes(cw, codeval, 'public', hex, unix_only=True)
305305

306306

307+
# python3 -c 'import signal;print(dict(sorted((s, int(getattr(signal, s))) for s in dir(signal) if s.startswith("SIG") and not s.startswith("SIG_"))))'
308+
# Python 3.12.3 [GCC 13.3.0] on linux 6.8.0
309+
SIG_codes_linux = {'SIGABRT': 6, 'SIGALRM': 14, 'SIGBUS': 7, 'SIGCHLD': 17, 'SIGCLD': 17, 'SIGCONT': 18, 'SIGFPE': 8, 'SIGHUP': 1, 'SIGILL': 4, 'SIGINT': 2, 'SIGIO': 29, 'SIGIOT': 6, 'SIGKILL': 9, 'SIGPIPE': 13, 'SIGPOLL': 29, 'SIGPROF': 27, 'SIGPWR': 30, 'SIGQUIT': 3, 'SIGRTMAX': 64, 'SIGRTMIN': 34, 'SIGSEGV': 11, 'SIGSTKFLT': 16, 'SIGSTOP': 19, 'SIGSYS': 31, 'SIGTERM': 15, 'SIGTRAP': 5, 'SIGTSTP': 20, 'SIGTTIN': 21, 'SIGTTOU': 22, 'SIGURG': 23, 'SIGUSR1': 10, 'SIGUSR2': 12, 'SIGVTALRM': 26, 'SIGWINCH': 28, 'SIGXCPU': 24, 'SIGXFSZ': 25}
310+
# Python 3.9.6 [Clang 17.0.0] on darwin 24.3.0
311+
SIG_codes_darwin = {'SIGABRT': 6, 'SIGALRM': 14, 'SIGBUS': 10, 'SIGCHLD': 20, 'SIGCONT': 19, 'SIGEMT': 7, 'SIGFPE': 8, 'SIGHUP': 1, 'SIGILL': 4, 'SIGINFO': 29, 'SIGINT': 2, 'SIGIO': 23, 'SIGIOT': 6, 'SIGKILL': 9, 'SIGPIPE': 13, 'SIGPROF': 27, 'SIGQUIT': 3, 'SIGSEGV': 11, 'SIGSTOP': 17, 'SIGSYS': 12, 'SIGTERM': 15, 'SIGTRAP': 5, 'SIGTSTP': 18, 'SIGTTIN': 21, 'SIGTTOU': 22, 'SIGURG': 16, 'SIGUSR1': 30, 'SIGUSR2': 31, 'SIGVTALRM': 26, 'SIGWINCH': 28, 'SIGXCPU': 24, 'SIGXFSZ': 25}
312+
# Python 3.6.8 [MSC v.1916 64 bit (AMD64)] on win32
313+
SIG_codes_windows = {'SIGABRT': 22, 'SIGBREAK': 21, 'SIGFPE': 8, 'SIGILL': 4, 'SIGINT': 2, 'SIGSEGV': 11, 'SIGTERM': 15}
314+
315+
def generate_signal_codes(cw):
316+
codeval = {}
317+
for idx, SIG_codes in [(linux_idx, SIG_codes_linux), (darwin_idx, SIG_codes_darwin), (windows_idx, SIG_codes_windows)]:
318+
for name in SIG_codes:
319+
set_value(codeval, name, SIG_codes[name], idx)
320+
codeval = OrderedDict(sorted(codeval.items()))
321+
generate_codes(cw, codeval, 'public', str)
322+
323+
324+
def generate_supported_signals(cw):
325+
for system, SIG_codes in [("Linux", SIG_codes_linux), ("MacOS", SIG_codes_darwin), ("Windows", SIG_codes_windows)]:
326+
cw.writeline(f'[SupportedOSPlatform("{system.lower()}")]')
327+
cw.writeline(f'private static readonly int[] _PySupportedSignals_{system} = [')
328+
cw.indent()
329+
cw.writeline(', '.join(sorted(SIG_codes)))
330+
cw.dedent()
331+
cw.writeline('];')
332+
cw.writeline()
333+
334+
307335
def main():
308336
return generate(
309337
("Errno Codes", generate_errno_codes),
@@ -315,6 +343,8 @@ def main():
315343
("Directory Notify Flags", generate_DN_flags),
316344
("LOCK Flags", generate_LOCK_flags),
317345
("TIOC Commands", generate_TIOC_commands),
346+
("Signal Codes", generate_signal_codes),
347+
("Supported Signals", generate_supported_signals),
318348
)
319349

320350
if __name__ == "__main__":

src/core/IronPython.Modules/SimpleSignalState.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#if FEATURE_PROCESS
88

99
using System;
10+
using System.Runtime.InteropServices;
1011

1112
using IronPython.Runtime;
1213

@@ -21,11 +22,12 @@ public SimpleSignalState(PythonContext pc)
2122

2223

2324
private void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e) {
24-
int pySignal = e.SpecialKey switch {
25-
ConsoleSpecialKey.ControlC => SIGINT,
26-
ConsoleSpecialKey.ControlBreak => SIGBREAK,
27-
_ => throw new InvalidOperationException("unreachable"),
28-
};
25+
int pySignal = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? SIGINT
26+
: e.SpecialKey switch {
27+
ConsoleSpecialKey.ControlC => SIGINT,
28+
ConsoleSpecialKey.ControlBreak => SIGBREAK,
29+
_ => throw new InvalidOperationException("unreachable"),
30+
};
2931

3032
lock (PySignalToPyHandler) {
3133
if (PySignalToPyHandler[pySignal].GetType() == typeof(int)) {

src/core/IronPython.Modules/signal.cs

+236-25
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,191 @@ private static PythonSignalState MakeSimpleSignalState(PythonContext context) {
8787
}
8888

8989

90-
//Python signals
91-
public const int NSIG = 23;
92-
public const int SIGABRT = 22;
93-
public const int SIGBREAK = 21;
94-
public const int SIGFPE = 8;
95-
public const int SIGILL = 4;
96-
public const int SIGINT = 2;
97-
public const int SIGSEGV = 11;
98-
public const int SIGTERM = 15;
90+
#region Generated Signal Codes
91+
92+
// *** BEGIN GENERATED CODE ***
93+
// generated by function: generate_signal_codes from: generate_os_codes.py
94+
95+
96+
public static int SIGABRT => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 22 : 6;
97+
98+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
99+
[SupportedOSPlatform("linux")]
100+
[SupportedOSPlatform("macos")]
101+
public static int SIGALRM => 14;
102+
103+
[PythonHidden(PlatformsAttribute.PlatformFamily.Unix)]
104+
[SupportedOSPlatform("windows")]
105+
public static int SIGBREAK => 21;
106+
107+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
108+
[SupportedOSPlatform("linux")]
109+
[SupportedOSPlatform("macos")]
110+
public static int SIGBUS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 10 : 7;
111+
112+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
113+
[SupportedOSPlatform("linux")]
114+
[SupportedOSPlatform("macos")]
115+
public static int SIGCHLD => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 20 : 17;
116+
117+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.MacOSX)]
118+
[SupportedOSPlatform("linux")]
119+
public static int SIGCLD => 17;
120+
121+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
122+
[SupportedOSPlatform("linux")]
123+
[SupportedOSPlatform("macos")]
124+
public static int SIGCONT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 19 : 18;
125+
126+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.Unix)]
127+
[SupportedOSPlatform("macos")]
128+
public static int SIGEMT => 7;
129+
130+
public static int SIGFPE => 8;
131+
132+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
133+
[SupportedOSPlatform("linux")]
134+
[SupportedOSPlatform("macos")]
135+
public static int SIGHUP => 1;
136+
137+
public static int SIGILL => 4;
138+
139+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.Unix)]
140+
[SupportedOSPlatform("macos")]
141+
public static int SIGINFO => 29;
142+
143+
public static int SIGINT => 2;
144+
145+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
146+
[SupportedOSPlatform("linux")]
147+
[SupportedOSPlatform("macos")]
148+
public static int SIGIO => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 23 : 29;
149+
150+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
151+
[SupportedOSPlatform("linux")]
152+
[SupportedOSPlatform("macos")]
153+
public static int SIGIOT => 6;
154+
155+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
156+
[SupportedOSPlatform("linux")]
157+
[SupportedOSPlatform("macos")]
158+
public static int SIGKILL => 9;
159+
160+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
161+
[SupportedOSPlatform("linux")]
162+
[SupportedOSPlatform("macos")]
163+
public static int SIGPIPE => 13;
164+
165+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.MacOSX)]
166+
[SupportedOSPlatform("linux")]
167+
public static int SIGPOLL => 29;
168+
169+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
170+
[SupportedOSPlatform("linux")]
171+
[SupportedOSPlatform("macos")]
172+
public static int SIGPROF => 27;
173+
174+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.MacOSX)]
175+
[SupportedOSPlatform("linux")]
176+
public static int SIGPWR => 30;
177+
178+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
179+
[SupportedOSPlatform("linux")]
180+
[SupportedOSPlatform("macos")]
181+
public static int SIGQUIT => 3;
182+
183+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.MacOSX)]
184+
[SupportedOSPlatform("linux")]
185+
public static int SIGRTMAX => 64;
186+
187+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.MacOSX)]
188+
[SupportedOSPlatform("linux")]
189+
public static int SIGRTMIN => 34;
190+
191+
public static int SIGSEGV => 11;
192+
193+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows, PlatformID.MacOSX)]
194+
[SupportedOSPlatform("linux")]
195+
public static int SIGSTKFLT => 16;
196+
197+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
198+
[SupportedOSPlatform("linux")]
199+
[SupportedOSPlatform("macos")]
200+
public static int SIGSTOP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 17 : 19;
201+
202+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
203+
[SupportedOSPlatform("linux")]
204+
[SupportedOSPlatform("macos")]
205+
public static int SIGSYS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 12 : 31;
206+
207+
public static int SIGTERM => 15;
208+
209+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
210+
[SupportedOSPlatform("linux")]
211+
[SupportedOSPlatform("macos")]
212+
public static int SIGTRAP => 5;
213+
214+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
215+
[SupportedOSPlatform("linux")]
216+
[SupportedOSPlatform("macos")]
217+
public static int SIGTSTP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 18 : 20;
218+
219+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
220+
[SupportedOSPlatform("linux")]
221+
[SupportedOSPlatform("macos")]
222+
public static int SIGTTIN => 21;
223+
224+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
225+
[SupportedOSPlatform("linux")]
226+
[SupportedOSPlatform("macos")]
227+
public static int SIGTTOU => 22;
228+
229+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
230+
[SupportedOSPlatform("linux")]
231+
[SupportedOSPlatform("macos")]
232+
public static int SIGURG => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 16 : 23;
233+
234+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
235+
[SupportedOSPlatform("linux")]
236+
[SupportedOSPlatform("macos")]
237+
public static int SIGUSR1 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 30 : 10;
238+
239+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
240+
[SupportedOSPlatform("linux")]
241+
[SupportedOSPlatform("macos")]
242+
public static int SIGUSR2 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 31 : 12;
243+
244+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
245+
[SupportedOSPlatform("linux")]
246+
[SupportedOSPlatform("macos")]
247+
public static int SIGVTALRM => 26;
248+
249+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
250+
[SupportedOSPlatform("linux")]
251+
[SupportedOSPlatform("macos")]
252+
public static int SIGWINCH => 28;
253+
254+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
255+
[SupportedOSPlatform("linux")]
256+
[SupportedOSPlatform("macos")]
257+
public static int SIGXCPU => 24;
258+
259+
[PythonHidden(PlatformsAttribute.PlatformFamily.Windows)]
260+
[SupportedOSPlatform("linux")]
261+
[SupportedOSPlatform("macos")]
262+
public static int SIGXFSZ => 25;
263+
264+
// *** END GENERATED CODE ***
265+
266+
#endregion
267+
268+
public static int NSIG => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 23 : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 32 : 65;
269+
99270
public const int SIG_DFL = 0;
100271
public const int SIG_IGN = 1;
101272

102-
//Windows signals
273+
274+
// Windows signals
103275
[SupportedOSPlatform("windows"), PythonHidden(PlatformsAttribute.PlatformFamily.Unix)]
104276
public const int CTRL_C_EVENT = 0;
105277
[SupportedOSPlatform("windows"), PythonHidden(PlatformsAttribute.PlatformFamily.Unix)]
@@ -172,10 +344,10 @@ public static object default_int_handlerImpl(int signalnum, TraceBackFrame? fram
172344
""")]
173345
public static object? signal(CodeContext/*!*/ context, int signalnum, object? action) {
174346
// Negative scenarios - signalnum
175-
if (signalnum <= 0 || signalnum >= NSIG) {
176-
throw PythonOps.ValueError("signal number out of range");
177-
} else if (Array.IndexOf(_PySupportedSignals, signalnum) == -1) {
178-
throw new RuntimeException("no IronPython support for given signal");
347+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
348+
if (Array.IndexOf(_PySupportedSignals_Windows, signalnum) == -1) throw PythonOps.ValueError("invalid signal value");
349+
} else {
350+
if (signalnum <= 0 || signalnum >= NSIG) throw PythonOps.ValueError("signal number out of range");
179351
}
180352
// Negative scenarios - action
181353
if (action == null) {
@@ -198,10 +370,15 @@ public static object default_int_handlerImpl(int signalnum, TraceBackFrame? fram
198370
}
199371
}
200372

373+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
374+
// These signals cannot be handled
375+
if (signalnum == SIGKILL || signalnum == SIGSTOP) throw PythonNT.GetOsError(PythonErrno.EINVAL);
376+
}
201377
object? last_handler = null;
202378
lock (GetPythonSignalState(context).PySignalToPyHandler) {
203379
// CPython returns the previous handler for the signal
204380
last_handler = getsignal(context, signalnum);
381+
if (last_handler is null) throw PythonNT.GetOsError(PythonErrno.EINVAL);
205382
// Set the new action
206383
GetPythonSignalState(context).PySignalToPyHandler[signalnum] = action;
207384
}
@@ -250,20 +427,54 @@ private class PythonSignalState {
250427

251428
public PythonSignalState(PythonContext pc) {
252429
SignalPythonContext = pc;
253-
PySignalToPyHandler = new Dictionary<int, object>() {
254-
{ SIGABRT, SIG_DFL},
255-
{ SIGBREAK, SIG_DFL},
256-
{ SIGFPE, SIG_DFL},
257-
{ SIGILL, SIG_DFL},
258-
{ SIGINT, default_int_handler},
259-
{ SIGSEGV, SIG_DFL},
260-
{ SIGTERM, SIG_DFL},
261-
};
430+
int[] sigs = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? _PySupportedSignals_Windows
431+
: RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? _PySupportedSignals_MacOS
432+
: RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? _PySupportedSignals_Linux
433+
: throw new NotSupportedException("Unsupported platform for signal module");
434+
435+
PySignalToPyHandler = new Dictionary<int, object>(sigs.Length);
436+
object sig_dfl = ScriptingRuntimeHelpers.Int32ToObject(SIG_DFL);
437+
object sig_ign = ScriptingRuntimeHelpers.Int32ToObject(SIG_IGN);
438+
foreach (int sig in sigs) {
439+
PySignalToPyHandler[sig] = sig_dfl;
440+
}
441+
PySignalToPyHandler[SIGINT] = default_int_handler;
442+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
443+
PySignalToPyHandler[SIGPIPE] = sig_ign;
444+
PySignalToPyHandler[SIGXFSZ] = sig_ign;
445+
}
446+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
447+
PySignalToPyHandler.Remove(SIGKILL);
448+
PySignalToPyHandler.Remove(SIGSTOP);
449+
}
262450
}
263451
}
264452

265-
// List of all Signals CPython supports on Windows. Notice the addition of '6'
266-
private static readonly int[] _PySupportedSignals = { SIGABRT, SIGBREAK, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, 6 };
453+
454+
#region Generated Supported Signals
455+
456+
// *** BEGIN GENERATED CODE ***
457+
// generated by function: generate_supported_signals from: generate_os_codes.py
458+
459+
[SupportedOSPlatform("linux")]
460+
private static readonly int[] _PySupportedSignals_Linux = [
461+
SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGIO, SIGIOT, SIGKILL, SIGPIPE, SIGPOLL, SIGPROF, SIGPWR, SIGQUIT, SIGRTMAX, SIGRTMIN, SIGSEGV, SIGSTKFLT, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ
462+
];
463+
464+
[SupportedOSPlatform("macos")]
465+
private static readonly int[] _PySupportedSignals_MacOS = [
466+
SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGEMT, SIGFPE, SIGHUP, SIGILL, SIGINFO, SIGINT, SIGIO, SIGIOT, SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ
467+
];
468+
469+
[SupportedOSPlatform("windows")]
470+
private static readonly int[] _PySupportedSignals_Windows = [
471+
SIGABRT, SIGBREAK, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM
472+
];
473+
474+
475+
// *** END GENERATED CODE ***
476+
477+
#endregion
267478

268479
// Signature of Python functions that signal.signal(...) expects to be given
269480
private delegate object PySignalHandler(int signalnum, TraceBackFrame? frame);

0 commit comments

Comments
 (0)