Skip to content

Commit

Permalink
Fix for pypy: don't use pythonapi on pypy.
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanslenders committed Dec 13, 2023
1 parent 1c6d094 commit 6a24c99
Showing 1 changed file with 46 additions and 32 deletions.
78 changes: 46 additions & 32 deletions src/prompt_toolkit/application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
sleep,
)
from contextlib import ExitStack, contextmanager
from ctypes import c_int, c_void_p, pythonapi
from subprocess import Popen
from traceback import format_tb
from typing import (
Expand Down Expand Up @@ -103,21 +102,6 @@
_SIGTSTP = getattr(signal, "SIGTSTP", None)


# The following functions are part of the stable ABI since python 3.2
# See: https://docs.python.org/3/c-api/sys.html#c.PyOS_getsig

# PyOS_sighandler_t PyOS_getsig(int i)
pythonapi.PyOS_getsig.restype = c_void_p
pythonapi.PyOS_getsig.argtypes = (c_int,)

# PyOS_sighandler_t PyOS_setsig(int i, PyOS_sighandler_t h)
pythonapi.PyOS_setsig.restype = c_void_p
pythonapi.PyOS_setsig.argtypes = (
c_int,
c_void_p,
)


class Application(Generic[_AppResult]):
"""
The main Application class!
Expand Down Expand Up @@ -823,22 +807,19 @@ def set_is_running() -> Iterator[None]:
@contextmanager
def set_handle_sigint(loop: AbstractEventLoop) -> Iterator[None]:
if handle_sigint:
# save sigint handlers (python and os level)
# See: https://github.com/prompt-toolkit/python-prompt-toolkit/issues/1576
sigint = signal.getsignal(signal.SIGINT)
sigint_os = pythonapi.PyOS_getsig(signal.SIGINT)
loop.add_signal_handler(
signal.SIGINT,
lambda *_: loop.call_soon_threadsafe(
self.key_processor.send_sigint
),
)
try:
yield
finally:
loop.remove_signal_handler(signal.SIGINT)
signal.signal(signal.SIGINT, sigint)
pythonapi.PyOS_setsig(signal.SIGINT, sigint_os)
with _restore_sigint_from_ctypes():
# save sigint handlers (python and os level)
# See: https://github.com/prompt-toolkit/python-prompt-toolkit/issues/1576
loop.add_signal_handler(
signal.SIGINT,
lambda *_: loop.call_soon_threadsafe(
self.key_processor.send_sigint
),
)
try:
yield
finally:
loop.remove_signal_handler(signal.SIGINT)
else:
yield

Expand Down Expand Up @@ -1609,3 +1590,36 @@ def attach_winch_signal_handler(
previous_winch_handler._callback,
*previous_winch_handler._args,
)


@contextmanager
def _restore_sigint_from_ctypes() -> Generator[None, None, None]:
# The following functions are part of the stable ABI since python 3.2
# See: https://docs.python.org/3/c-api/sys.html#c.PyOS_getsig
# Inline import: these are not available on Pypy.
try:
from ctypes import c_int, c_void_p, pythonapi
except ImportError:
# Any of the above imports don't exist? Don't do anything here.
yield
return

# PyOS_sighandler_t PyOS_getsig(int i)
pythonapi.PyOS_getsig.restype = c_void_p
pythonapi.PyOS_getsig.argtypes = (c_int,)

# PyOS_sighandler_t PyOS_setsig(int i, PyOS_sighandler_t h)
pythonapi.PyOS_setsig.restype = c_void_p
pythonapi.PyOS_setsig.argtypes = (
c_int,
c_void_p,
)

sigint = signal.getsignal(signal.SIGINT)
sigint_os = pythonapi.PyOS_getsig(signal.SIGINT)

try:
yield
finally:
signal.signal(signal.SIGINT, sigint)

This comment has been minimized.

Copy link
@quark-zju

quark-zju Apr 9, 2024

Contributor

This might crash if a C/C++/Rust program installs a native (non-Python) SIGINT handler then calls into this Python code (ex. via IPython).

sigint = signal.getsignal(signal.SIGINT)  # None, since the SIGINT handler is not installed by Python
signal.signal(signal.SIGINT, sigint) # TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object

EDIT: Sent #1865 as a fix.

pythonapi.PyOS_setsig(signal.SIGINT, sigint_os)

0 comments on commit 6a24c99

Please sign in to comment.