From dda41ff956eb9d94b6d5bdb04a28dfa8ead3c772 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Fri, 8 Nov 2024 12:14:03 -0800 Subject: [PATCH 1/5] Let subprocess launched by run handle its own sigint Otherwise, the subprocess is aborted by the parent process. This change makes `spin run python -i ...` usable. Closes #235 --- spin/cmds/meson.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spin/cmds/meson.py b/spin/cmds/meson.py index cd9c43d..86354bf 100644 --- a/spin/cmds/meson.py +++ b/spin/cmds/meson.py @@ -4,6 +4,7 @@ import os import re import shutil +import signal import sys from enum import Enum from pathlib import Path @@ -815,6 +816,10 @@ def run(ctx, *, args, build_dir=None): cmd_args = ["bash", "-c", cmd_args] _set_pythonpath(build_dir, quiet=True) + + # Let the subprocess handle its own signals + signal.signal(signal.SIGINT, signal.SIG_IGN) + p = _run(cmd_args, echo=False, shell=shell, sys_exit=False) # Is the user trying to run a Python script, without calling the Python interpreter? From f266a7daf578f5455b3a86abe255b6d006d40f70 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Fri, 8 Nov 2024 18:18:18 -0800 Subject: [PATCH 2/5] Pass sigint to subprocess --- spin/cmds/meson.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spin/cmds/meson.py b/spin/cmds/meson.py index 86354bf..3bfd6fa 100644 --- a/spin/cmds/meson.py +++ b/spin/cmds/meson.py @@ -820,7 +820,13 @@ def run(ctx, *, args, build_dir=None): # Let the subprocess handle its own signals signal.signal(signal.SIGINT, signal.SIG_IGN) - p = _run(cmd_args, echo=False, shell=shell, sys_exit=False) + def attach_sigint(): + # Reset SIGINT handler to default + signal.signal(signal.SIGINT, signal.SIG_DFL) + + p = _run( + cmd_args, echo=False, shell=shell, sys_exit=False, preexec_fn=attach_sigint + ) # Is the user trying to run a Python script, without calling the Python interpreter? executable = args[0] From 1e985f44adae1eaee53a4bb85c2ebb8f9feb986f Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sat, 16 Nov 2024 17:40:25 -0800 Subject: [PATCH 3/5] Handle Ctrl-C on Windows --- spin/cmds/meson.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/spin/cmds/meson.py b/spin/cmds/meson.py index 3bfd6fa..1f51f2e 100644 --- a/spin/cmds/meson.py +++ b/spin/cmds/meson.py @@ -5,6 +5,7 @@ import re import shutil import signal +import subprocess import sys from enum import Enum from pathlib import Path @@ -817,17 +818,44 @@ def run(ctx, *, args, build_dir=None): _set_pythonpath(build_dir, quiet=True) - # Let the subprocess handle its own signals - signal.signal(signal.SIGINT, signal.SIG_IGN) + is_windows = sys.platform == "win32" + is_posix = not is_windows + + if is_posix: + # Let the subprocess handle its own signals + signal.signal(signal.SIGINT, signal.SIG_IGN) def attach_sigint(): # Reset SIGINT handler to default signal.signal(signal.SIGINT, signal.SIG_DFL) + kwargs = ( + {"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP} if is_windows else {} + ) + + # --- launch subprocess --- p = _run( - cmd_args, echo=False, shell=shell, sys_exit=False, preexec_fn=attach_sigint + cmd_args, + echo=False, + shell=shell, + sys_exit=False, + preexec_fn=attach_sigint if is_posix else None, + **kwargs, ) + # Handle Ctrl+C (SIGINT) on Windows + def windows_ctrl_handler(ctrl_type): + if ctrl_type == signal.CTRL_C_EVENT: + # Forward CTRL_BREAK_EVENT to the child process + p.send_signal(signal.CTRL_BREAK_EVENT) + return True + return False + + if is_windows: + import win32api + + win32api.SetConsoleCtrlHandler(windows_ctrl_handler, True) + # Is the user trying to run a Python script, without calling the Python interpreter? executable = args[0] if ( From 5b60ef2473d7149a27567f989d6ce2040d25d77a Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sat, 16 Nov 2024 17:43:31 -0800 Subject: [PATCH 4/5] Require pywin32 on Windows --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index a7ac6e9..76023b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ dependencies = [ "click", "tomli; python_version < '3.11'", "colorama; platform_system == 'Windows'", + "pywin32; platform_system == 'Windows'", "importlib_metadata >= 7" ] dynamic = ['version'] From 5c0a79df9bcdb23997ba73383deb219f3dd95271 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sun, 17 Nov 2024 12:21:10 -0800 Subject: [PATCH 5/5] Remove custom Windows interrupt handling --- pyproject.toml | 1 - spin/cmds/meson.py | 26 ++++---------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 76023b5..a7ac6e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ dependencies = [ "click", "tomli; python_version < '3.11'", "colorama; platform_system == 'Windows'", - "pywin32; platform_system == 'Windows'", "importlib_metadata >= 7" ] dynamic = ['version'] diff --git a/spin/cmds/meson.py b/spin/cmds/meson.py index 1f51f2e..c2afa01 100644 --- a/spin/cmds/meson.py +++ b/spin/cmds/meson.py @@ -5,7 +5,6 @@ import re import shutil import signal -import subprocess import sys from enum import Enum from pathlib import Path @@ -819,43 +818,26 @@ def run(ctx, *, args, build_dir=None): _set_pythonpath(build_dir, quiet=True) is_windows = sys.platform == "win32" - is_posix = not is_windows - if is_posix: + if not is_windows: # Let the subprocess handle its own signals + # Except on Windows, where it already seems to work as intended, + # and `preexec_fn` is not supported signal.signal(signal.SIGINT, signal.SIG_IGN) def attach_sigint(): # Reset SIGINT handler to default signal.signal(signal.SIGINT, signal.SIG_DFL) - kwargs = ( - {"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP} if is_windows else {} - ) - # --- launch subprocess --- p = _run( cmd_args, echo=False, shell=shell, sys_exit=False, - preexec_fn=attach_sigint if is_posix else None, - **kwargs, + preexec_fn=None if is_windows else attach_sigint, ) - # Handle Ctrl+C (SIGINT) on Windows - def windows_ctrl_handler(ctrl_type): - if ctrl_type == signal.CTRL_C_EVENT: - # Forward CTRL_BREAK_EVENT to the child process - p.send_signal(signal.CTRL_BREAK_EVENT) - return True - return False - - if is_windows: - import win32api - - win32api.SetConsoleCtrlHandler(windows_ctrl_handler, True) - # Is the user trying to run a Python script, without calling the Python interpreter? executable = args[0] if (