The standard way of interrupting a long running script in a Python shell is to press Ctrl+C which signals a keyboard interrupt and the interpreter raises a KeyboardInterrupt exception. I was trying for a while to find a way to raise a KeyboardInterrupt execption at the remote python engine without much success. The way IDLE handles this, is by running a separate thread in the server, which waits for such a signal to arrive from IDLE and then uses the interrupt_main function of the threading module. I thought that having a separate thread just for this purpose would slow down the execution of scripts and the PyScripter solution of reinitializing the engine was good enough.
Resently, a user has posted an issue about this at PyScripter’s bug tracker and I had another look at the problem. So I found a solution using Windows API and avoiding run overheads, For the benefit of Windows hackers I show the code below:
function CtrlHandler( fdwCtrlType : dword): LongBool; stdcall;
begin
Result := True;
end;
AttachConsole := GetProcAddress (GetModuleHandle ('kernel32.dll'), 'AttachConsole');
if Assigned(AttachConsole) then
try
OSCheck(AttachConsole(fRemotePython.ServerProcess.ProcessInfo.dwProcessId));
OSCheck(SetConsoleCtrlHandler(@CtrlHandler, True));
try
OSCheck(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0));
Sleep(100);
finally
OSCheck(SetConsoleCtrlHandler(@CtrlHandler, False));
OSCheck(FreeConsole);
end;
except
end;
Note that the CtrlHandler is needed to avoid killing PyScripter itself. It took me a while to work out this piece of code since, I could not find such code sample (only pieces of the puzzle) in the Internet. The only solution I found involved injecting code into the server process.
So starting from the forthcoming version 2.5, the Abort command will result in a KeyboardInterrupt exeption in script is run or debugged. So there is no need to reinitialize the engine to stop a script. After the script stops you can enter the Post-Mortem mode to see what the script was doing when it received the Keyboard interrupt.
I could have added a shortcut Ctrl+C to the interpreter for raising a Keyboard interrupt, but the Ctrl-C shortcut in GUI applications is associated with the copy command. So the Abort command will achieve the same thing as pressing Ctrl+C in Python Shell. When a scrip is stopped at a breakpoint, the Abort command will work as before.