1. Restore the capability to run and debug without a subprocess.

2. Add an indicator to the shell startup notice when running w/o
   subprocess.
3. Improve exception reporting when running a command or script from the
   command line.
4. Clarify the fact that breakpoints set or cleared after a file is
   saved will revert to the saved state if the file is closed without
   re-saving.
5. If user tries to exit or restart when user code is running, interrupt
   the user code.  This helps to eliminate occasional hanging
   subprocesses on Windows (except for Freddy :).

M NEWS.txt
M PyShell.py
M ScriptBinding.py
This commit is contained in:
Kurt B. Kaiser 2003-05-15 03:19:42 +00:00
parent f655dff807
commit 7f38ec0849
3 changed files with 98 additions and 54 deletions

View File

@ -7,6 +7,22 @@ What's New in IDLEfork 0.9b1?
*Release date: XX-XXX-2003*
- Interrupt the subprocess if it is running when the user attempts to
restart the shell, run a module, or exit.
- Improved exception reporting when running commands or scripts from the
command line.
- Added a comment to the shell startup header to indicate when IDLE is not
using the subprocess. (For now, set PyShell.use_subprocess to False to run
in this mode.)
- Restore the ability to run without the subprocess. This can be important for
some platforms or configurations. (Running without the subprocess allows the
debugger to trace through parts of IDLE itself, which may or may not be
desirable, depending on your point of view. In addition, the traditional
reload/import tricks must be use if user source code is changed.)
- Improve the error message a user gets when saving a file with non-ASCII
characters and no source encoding is specified. Done by adding a dialog
'EncodingMessage', which contains the line to add in a fixed-font entry

View File

@ -31,6 +31,7 @@ from configHandler import idleConf
import idlever
import rpc
import Debugger
import RemoteDebugger
IDENTCHARS = string.ascii_letters + string.digits + "_"
@ -160,9 +161,10 @@ class PyShellEditorWindow(EditorWindow):
# a temporary file save feature the save breaks functionality
# needs to be re-verified, since the breaks at the time the
# temp file is created may differ from the breaks at the last
# permanent save of the file. A break introduced after a save
# will be effective, but not persistent. This is necessary to
# keep the saved breaks synched with the saved file.
# permanent save of the file. Currently, a break introduced
# after a save will be effective, but not persistent.
# This is necessary to keep the saved breaks synched with the
# saved file.
#
# Breakpoints are set as tagged ranges in the text. Certain
# kinds of edits cause these ranges to be deleted: Inserting
@ -361,17 +363,18 @@ class ModifiedInterpreter(InteractiveInterpreter):
# Kill subprocess, spawn a new one, accept connection.
self.rpcclt.close()
self.unix_terminate()
self.tkconsole.executing = False
console = self.tkconsole
console.executing = False
self.spawn_subprocess()
self.rpcclt.accept()
self.transfer_path()
# annotate restart in shell window and mark it
console = self.tkconsole
console.text.delete("iomark", "end-1c")
halfbar = ((int(console.width) - 16) // 2) * '='
console.write(halfbar + ' RESTART ' + halfbar)
console.text.mark_set("restart", "end-1c")
console.text.mark_gravity("restart", "left")
console.showprompt()
# restart subprocess debugger
if debug:
# Restarted debugger connects to current instance of debug GUI
@ -489,8 +492,9 @@ class ModifiedInterpreter(InteractiveInterpreter):
code = compile(source, filename, "exec")
except (OverflowError, SyntaxError):
self.tkconsole.resetoutput()
console = self.tkconsole.console
print >>console, 'Traceback (most recent call last):'
tkerr = self.tkconsole.stderr
print>>tkerr, '*** Error in script or command!\n'
print>>tkerr, 'Traceback (most recent call last):'
InteractiveInterpreter.showsyntaxerror(self, filename)
self.tkconsole.showprompt()
else:
@ -608,30 +612,34 @@ class ModifiedInterpreter(InteractiveInterpreter):
warnings.filters[:] = self.save_warnings_filters
self.save_warnings_filters = None
debugger = self.debugger
self.tkconsole.beginexecuting()
try:
if not debugger and self.rpcclt is not None:
self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
(code,), {})
elif debugger:
debugger.run(code, self.locals)
else:
exec code in self.locals
except SystemExit:
if tkMessageBox.askyesno(
"Exit?",
"Do you want to exit altogether?",
default="yes",
master=self.tkconsole.text):
raise
else:
self.tkconsole.beginexecuting()
try:
if not debugger and self.rpcclt is not None:
self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
(code,), {})
elif debugger:
debugger.run(code, self.locals)
else:
exec code in self.locals
except SystemExit:
if tkMessageBox.askyesno(
"Exit?",
"Do you want to exit altogether?",
default="yes",
master=self.tkconsole.text):
raise
else:
self.showtraceback()
except:
self.showtraceback()
except:
self.showtraceback()
finally:
if not use_subprocess:
self.tkconsole.endexecuting()
def write(self, s):
"Override base class method"
self.tkconsole.console.write(s)
self.tkconsole.stderr.write(s)
class PyShell(OutputWindow):
@ -741,22 +749,13 @@ class PyShell(OutputWindow):
self.set_debugger_indicator()
def open_debugger(self):
# XXX KBK 13Jun02 An RPC client always exists now? Open remote
# debugger and return...dike the rest of this fcn and combine
# with open_remote_debugger?
if self.interp.rpcclt:
return self.open_remote_debugger()
import Debugger
self.interp.setdebugger(Debugger.Debugger(self))
sys.ps1 = "[DEBUG ON]\n>>> "
self.showprompt()
self.set_debugger_indicator()
def open_remote_debugger(self):
gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt, self)
self.interp.setdebugger(gui)
# Load all PyShellEditorWindow breakpoints:
gui.load_breakpoints()
dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
self)
else:
dbg_gui = Debugger.Debugger(self)
self.interp.setdebugger(dbg_gui)
dbg_gui.load_breakpoints()
sys.ps1 = "[DEBUG ON]\n>>> "
self.showprompt()
self.set_debugger_indicator()
@ -779,18 +778,22 @@ class PyShell(OutputWindow):
"Kill?",
"The program is still running!\n Do you want to kill it?",
default="ok",
master=self.text)
parent=self.text)
if response == False:
return "cancel"
# interrupt the subprocess
self.closing = True
self.endexecuting()
return EditorWindow.close(self)
self.canceled = True
if use_subprocess:
self.interp.interrupt_subprocess()
return "cancel"
else:
return EditorWindow.close(self)
def _close(self):
"Extend EditorWindow._close(), shut down debugger and execution server"
self.close_debugger()
self.interp.kill_subprocess()
if use_subprocess:
self.interp.kill_subprocess()
# Restore std streams
sys.stdout = self.save_stdout
sys.stderr = self.save_stderr
@ -814,9 +817,13 @@ class PyShell(OutputWindow):
def begin(self):
self.resetoutput()
self.write("Python %s on %s\n%s\nIDLEfork %s\n" %
if use_subprocess:
nosub = ''
else:
nosub = "==== No Subprocess ===="
self.write("Python %s on %s\n%s\nIDLEfork %s %s\n" %
(sys.version, sys.platform, self.COPYRIGHT,
idlever.IDLE_VERSION))
idlever.IDLE_VERSION, nosub))
self.showprompt()
import Tkinter
Tkinter._default_root = None
@ -853,7 +860,7 @@ class PyShell(OutputWindow):
pass
if not (self.executing or self.reading):
self.resetoutput()
self.write("KeyboardInterrupt\n")
self.interp.write("KeyboardInterrupt\n")
self.showprompt()
return "break"
self.endoffile = 0
@ -997,8 +1004,16 @@ class PyShell(OutputWindow):
self.text.see("restart")
def restart_shell(self, event=None):
self.interp.restart_subprocess()
self.showprompt()
if self.executing:
self.cancel_callback()
# Wait for subprocess to interrupt and restart
# This can be a long time if shell is scrolling on a slow system
# XXX 14 May 03 KBK This delay (and one in ScriptBinding) could be
# shorter if we didn't print the KeyboardInterrupt on
# restarting while user code is running....
self.text.after(2000, self.interp.restart_subprocess)
else:
self.interp.restart_subprocess()
def showprompt(self):
self.resetoutput()
@ -1030,6 +1045,8 @@ class PyShell(OutputWindow):
pass
if self.canceled:
self.canceled = 0
if not use_subprocess:
raise KeyboardInterrupt
class PseudoFile:

View File

@ -22,6 +22,7 @@ import string
import tabnanny
import tokenize
import tkMessageBox
import PyShell
IDENTCHARS = string.ascii_letters + string.digits + "_"
@ -38,8 +39,6 @@ To fix case 2, change all tabs to spaces by using Select All followed \
by Untabify Region (both in the Edit menu)."""
# XXX 11Jun02 KBK TBD Implement stop-execution
class ScriptBinding:
menudefs = [
@ -124,7 +123,19 @@ class ScriptBinding:
flist = self.editwin.flist
shell = flist.open_shell()
interp = shell.interp
interp.restart_subprocess()
if PyShell.use_subprocess:
shell.restart_shell()
if shell.executing:
delay = 2700
else:
delay = 500
# Wait for the interrupt and reset to finish
shell.text.after(delay, self.run_module_event2, interp,
filename, code)
else:
self.run_module_event2(interp, filename, code)
def run_module_event2(self, interp, filename, code):
# XXX Too often this discards arguments the user just set...
interp.runcommand("""if 1:
_filename = %s