bpo-43356: Allow passing a signal number to interrupt_main() (GH-24755)

Also introduce a new C API ``PyErr_SetInterruptEx(int signum)``.
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 4e99a01..158672d 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -505,29 +505,73 @@
       single: SIGINT
       single: KeyboardInterrupt (built-in exception)
 
-   This function interacts with Python's signal handling.  It checks whether a
-   signal has been sent to the processes and if so, invokes the corresponding
-   signal handler.  If the :mod:`signal` module is supported, this can invoke a
-   signal handler written in Python.  In all cases, the default effect for
-   :const:`SIGINT` is to raise the  :exc:`KeyboardInterrupt` exception.  If an
-   exception is raised the error indicator is set and the function returns ``-1``;
-   otherwise the function returns ``0``.  The error indicator may or may not be
-   cleared if it was previously set.
+   This function interacts with Python's signal handling.
+
+   If the function is called from the main thread and under the main Python
+   interpreter, it checks whether a signal has been sent to the processes
+   and if so, invokes the corresponding signal handler.  If the :mod:`signal`
+   module is supported, this can invoke a signal handler written in Python.
+
+   The function attemps to handle all pending signals, and then returns ``0``.
+   However, if a Python signal handler raises an exception, the error
+   indicator is set and the function returns ``-1`` immediately (such that
+   other pending signals may not have been handled yet: they will be on the
+   next :c:func:`PyErr_CheckSignals()` invocation).
+
+   If the function is called from a non-main thread, or under a non-main
+   Python interpreter, it does nothing and returns ``0``.
+
+   This function can be called by long-running C code that wants to
+   be interruptible by user requests (such as by pressing Ctrl-C).
+
+   .. note::
+      The default Python signal handler for :const:`SIGINT` raises the
+      :exc:`KeyboardInterrupt` exception.
 
 
 .. c:function:: void PyErr_SetInterrupt()
 
    .. index::
+      module: signal
       single: SIGINT
       single: KeyboardInterrupt (built-in exception)
 
-   Simulate the effect of a :const:`SIGINT` signal arriving. The next time
-   :c:func:`PyErr_CheckSignals` is called,  the Python signal handler for
-   :const:`SIGINT` will be called.
+   Simulate the effect of a :const:`SIGINT` signal arriving.
+   This is equivalent to ``PyErr_SetInterruptEx(SIGINT)``.
 
-   If :const:`SIGINT` isn't handled by Python (it was set to
-   :data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), this function does
-   nothing.
+   .. note::
+      This function is async-signal-safe.  It can be called without
+      the :term:`GIL` and from a C signal handler.
+
+
+.. c:function:: int PyErr_SetInterruptEx(int signum)
+
+   .. index::
+      module: signal
+      single: KeyboardInterrupt (built-in exception)
+
+   Simulate the effect of a signal arriving. The next time
+   :c:func:`PyErr_CheckSignals` is called,  the Python signal handler for
+   the given signal number will be called.
+
+   This function can be called by C code that sets up its own signal handling
+   and wants Python signal handlers to be invoked as expected when an
+   interruption is requested (for example when the user presses Ctrl-C
+   to interrupt an operation).
+
+   If the given signal isn't handled by Python (it was set to
+   :data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), it will be ignored.
+
+   If *signum* is outside of the allowed range of signal numbers, ``-1``
+   is returned.  Otherwise, ``0`` is returned.  The error indicator is
+   never changed by this function.
+
+   .. note::
+      This function is async-signal-safe.  It can be called without
+      the :term:`GIL` and from a C signal handler.
+
+   .. versionadded:: 3.10
+
 
 .. c:function:: int PySignal_SetWakeupFd(int fd)
 
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index c2c9c6e..906b0a7 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -36,6 +36,7 @@
 PyCFunction_GetFlags
 PyCFunction_GetFunction
 PyCFunction_GetSelf
+PyCFunction_New
 PyCFunction_NewEx
 PyCFunction_Type
 PyCMethod_New
@@ -144,6 +145,7 @@
 PyErr_SetImportError
 PyErr_SetImportErrorSubclass
 PyErr_SetInterrupt
+PyErr_SetInterruptEx
 PyErr_SetNone
 PyErr_SetObject
 PyErr_SetString
diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst
index bd653ab..1e6452b 100644
--- a/Doc/library/_thread.rst
+++ b/Doc/library/_thread.rst
@@ -61,15 +61,27 @@
       :func:`sys.unraisablehook` is now used to handle unhandled exceptions.
 
 
-.. function:: interrupt_main()
+.. function:: interrupt_main(signum=signal.SIGINT, /)
 
-   Simulate the effect of a :data:`signal.SIGINT` signal arriving in the main
-   thread. A thread can use this function to interrupt the main thread.
+   Simulate the effect of a signal arriving in the main thread.
+   A thread can use this function to interrupt the main thread, though
+   there is no guarantee that the interruption will happen immediately.
 
-   If :data:`signal.SIGINT` isn't handled by Python (it was set to
+   If given, *signum* is the number of the signal to simulate.
+   If *signum* is not given, :data:`signal.SIGINT` is simulated.
+
+   If the given signal isn't handled by Python (it was set to
    :data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), this function does
    nothing.
 
+   .. versionchanged:: 3.10
+      The *signum* argument is added to customize the signal number.
+
+   .. note::
+      This does not emit the corresponding signal but schedules a call to
+      the associated handler (if it exists).
+      If you want to truly emit the signal, use :func:`signal.raise_signal`.
+
 
 .. function:: exit()
 
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index 814594a..db36f69 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -788,6 +788,13 @@
 module names.
 (Contributed by Victor Stinner in :issue:`42955`.)
 
+_thread
+-------
+
+:func:`_thread.interrupt_main` now takes an optional signal number to
+simulate (the default is still :data:`signal.SIGINT`).
+(Contributed by Antoine Pitrou in :issue:`43356`.)
+
 threading
 ---------
 
@@ -1210,6 +1217,11 @@
   object is an instance of :class:`set` but not an instance of a subtype.
   (Contributed by Pablo Galindo in :issue:`43277`.)
 
+* Added :c:func:`PyErr_SetInterruptEx` which allows passing a signal number
+  to simulate.
+  (Contributed by Antoine Pitrou in :issue:`43356`.)
+
+
 Porting to Python 3.10
 ----------------------