py.shell.Shell: how to interrupt running code

Hi!

I'm using py.shell.Shell within a larger application.

Is there a way to interrupt long running code?
Keyboard events are obviously not handled by wxPython while the code is running in the shell.
If I'm doing a Ctrl-C in the console window, the application is
terminated...
I've tried to place a try/except KeyboardInterrupt around some places
where the interpreter is called, but with no effect.

Something like PythonWin's "Break into running code" would be nice.
(If you don't know this: PythonWin is creating an icon in the system
  tray which offers this menu item. When you select this, it will send
  a KeyboardInterrupt to the code that is running in PythonWin's
  interactive window.)

Regards,

Dietmar

Hi,

Hi!

I'm using py.shell.Shell within a larger application.

Is there a way to interrupt long running code?
Keyboard events are obviously not handled by wxPython while the code is
running in the shell.
If I'm doing a Ctrl-C in the console window, the application is
terminated...
I've tried to place a try/except KeyboardInterrupt around some places
where the interpreter is called, but with no effect.

This is because it runs in the same instance of python and the same thread that your application is running in. So while code is running in the shell it is blocking your applications so no key events will be processed durning that time. To fix this py.shell would need to execute its code in a python shell that is running in a separate process and relay that back to the ui portion of the shell that is running in your process.

Cody

···

On Nov 14, 2009, at 5:58 AM, Dietmar Schwertberger wrote:

Cody Precord schrieb:

I'm using py.shell.Shell within a larger application.

Is there a way to interrupt long running code?
Keyboard events are obviously not handled by wxPython while the code is
running in the shell.
If I'm doing a Ctrl-C in the console window, the application is
terminated...
I've tried to place a try/except KeyboardInterrupt around some places
where the interpreter is called, but with no effect.

This is because it runs in the same instance of python and the same thread that your application is running in. So while code is running in the shell it is blocking your applications so no key events will be processed durning that time. To fix this py.shell would need to execute its code in a python shell that is running in a separate process and relay that back to the ui portion of the shell that is running in your process.

Thanks. Yes, I know about event handling and a separate process would
not help if the shell should be able to control the application.
A separate thread would also not be too nice as the shell then could not call any code that manipulates the GUI.
Therefore I tried with the console window as this will receive the
KeyboardInterrupt. It should somehow be possible that the
KeyboardInterrupt interrupts the running code and but is handled in
the interpreter before it propagates to the wx main loop. But by placing
try/except at some obvious places I could not achieve this.

If I have a standard console script, it's no problem.
E.g. this one will run an infinite loop until I hit Ctrl-C and then
continues with the raw_input:

import code
int = code.InteractiveInterpreter(locals=None)
try:
     source = "while True:print '.',\n"
     int.runsource(source)
except KeyboardInterrupt:
     print "Interrupted"

raw_input()

But if I try the same with py shell by inserting a try/except in the
following code and do a Ctrl-C in the console window, I can't catch
the interrupt:
...\Lib\site-packages\wx-2.8-msw-unicode\wx\py\interpreter.py
class Interpreter(InteractiveInterpreter):
     def runsource(self, source):
         """Compile and run source code in the interpreter."""
         stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr
         sys.stdin, sys.stdout, sys.stderr = \
                    self.stdin, self.stdout, self.stderr
         try:
             more = InteractiveInterpreter.runsource(self, source)
         except KeyboardInterrupt:
             print "KeyboardInterrupt"

Regards,

Dietmar

···

On Nov 14, 2009, at 5:58 AM, Dietmar Schwertberger wrote:

Hi,

Thanks. Yes, I know about event handling and a separate process would
not help if the shell should be able to control the application.
A separate thread would also not be too nice as the shell then could not
call any code that manipulates the GUI.
Therefore I tried with the console window as this will receive the
KeyboardInterrupt. It should somehow be possible that the
KeyboardInterrupt interrupts the running code and but is handled in
the interpreter before it propagates to the wx main loop. But by placing
try/except at some obvious places I could not achieve this.

If I have a standard console script, it's no problem.
E.g. this one will run an infinite loop until I hit Ctrl-C and then
continues with the raw_input:

import code
int = code.InteractiveInterpreter(locals=None)
try:
    source = "while True:print '.',\n"
    int.runsource(source)
except KeyboardInterrupt:
    print "Interrupted"

raw_input()

There is a major difference here. When your in the console and run 'python myscript.py' that forks of a separate child process from the console itself. So when you hit ctrl+c in the console it sends a signal to the child python process that is running your script which will then cause the KeyboardInterupt to be raised.

As mentioned py.shell is running in the same process as your main application. So if you are running an infinite loop in the shell it will be blocking the execution of any other code in your application. Hitting Ctrl+C here will cause a wx KeyEvent to be generated but since the mainloop is being blocked by the code in the shell the key events will and cannot be processed until the code being run by the shell has returned control to the main loop.

Cody

···

On Nov 14, 2009, at 10:10 AM, Dietmar Schwertberger wrote:

Cody Precord schrieb:

There is a major difference here. When your in the console and run 'python myscript.py' that forks of a separate child process from the console itself. So when you hit ctrl+c in the console it sends a signal to the child python process that is running your script which will then cause the KeyboardInterupt to be raised.

As mentioned py.shell is running in the same process as your main application. So if you are running an infinite loop in the shell it will be blocking the execution of any other code in your application. Hitting Ctrl+C here will cause a wx KeyEvent to be generated but since the mainloop is being blocked by the code in the shell the key events will and cannot be processed until the code being run by the shell has returned control to the main loop.

Yes, that's if the application's main window is the current window.
But I'm using a script with .py file extension (*), so I have a console
window with my app as well. If I do a Ctrl-C, the application terminates
immediately. Shouldn't it be possible to catch this event somehow?
In the wxWidgets documentation there are methods like
wxApp::OnUnhandledException. But from what I see, these are not
available in wxPython.

Regards,

Dietmar

(*) Or, I'm starting the script from a console window.
     Result is the same as with double-clicking the .py file.

Cody Precord schrieb:

There is a major difference here. When your in the console and run
'python myscript.py' that forks of a separate child process from the
console itself. So when you hit ctrl+c in the console it sends a
signal to the child python process that is running your script which
will then cause the KeyboardInterupt to be raised.

As mentioned py.shell is running in the same process as your main
application. So if you are running an infinite loop in the shell it
will be blocking the execution of any other code in your application.
Hitting Ctrl+C here will cause a wx KeyEvent to be generated but since
the mainloop is being blocked by the code in the shell the key events
will and cannot be processed until the code being run by the shell has
returned control to the main loop.

Yes, that's if the application's main window is the current window.
But I'm using a script with .py file extension (*), so I have a console
window with my app as well. If I do a Ctrl-C, the application terminates
immediately. Shouldn't it be possible to catch this event somehow?

No. It is being caught by a signal handler set up by the native UI library which is then causing a termination of the app. Python is not able to catch it in this case.

In the wxWidgets documentation there are methods like
wxApp::OnUnhandledException. But from what I see, these are not
available in wxPython.

And it's not for Python exceptions anyway.

···

On 11/14/09 10:14 AM, Dietmar Schwertberger wrote:

--
Robin Dunn
Software Craftsman

Robin Dunn schrieb:

Yes, that's if the application's main window is the current window.
But I'm using a script with .py file extension (*), so I have a console
window with my app as well. If I do a Ctrl-C, the application terminates
immediately. Shouldn't it be possible to catch this event somehow?

No. It is being caught by a signal handler set up by the native UI library which is then causing a termination of the app. Python is not able to catch it in this case.

Thanks for the hint. Long time since I did any signal handling in C.
I've been searching wxWidgets and wxPython for SIGINT.
There's an argument clearSigInt to wx.App. See below for the code.
If I set this to False, then pressing Ctrl-C in the console will
interrupt an endless loop running in the shell.
It will also interrupt any other Python code, especially any GUI code.
E.g. if I do a Ctrl-C in the console and then move the mouse pointer
over the GUI window, then my mouse handler will be interrupted.

The default signal handler is default_int_handler:
>>> import signal
>>> signal.getsignal(signal.SIGINT)
<built-in function default_int_handler>

It's also possible to ignore the signal:
signal.signal(signal.SIGINT, signal.SIG_IGN)

So, it seems that setting the handler to default_int_handler before the shell will execute any code could do the job. The rest of the time it
can remain at SIG_IGN.
The app can then only be terminated regularly by the GUI code or
alternatively by closing the console window.

Regards,

Dietmar

The code in _core.py:

class App(wx.PyApp):
[...]
     def __init__(self, redirect=_defRedirect, filename=None,
                  useBestVisual=False, clearSigInt=True):
[...]
         if clearSigInt:
             try:
                 import signal
                 signal.signal(signal.SIGINT, signal.SIG_DFL)
             except:
                 pass

···

On 11/14/09 10:14 AM, Dietmar Schwertberger wrote: