Commit 7f38ec08 authored by Kurt B. Kaiser's avatar Kurt B. Kaiser

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
parent f655dff8
...@@ -7,6 +7,22 @@ What's New in IDLEfork 0.9b1? ...@@ -7,6 +7,22 @@ What's New in IDLEfork 0.9b1?
*Release date: XX-XXX-2003* *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 - 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 characters and no source encoding is specified. Done by adding a dialog
'EncodingMessage', which contains the line to add in a fixed-font entry 'EncodingMessage', which contains the line to add in a fixed-font entry
......
...@@ -31,6 +31,7 @@ from configHandler import idleConf ...@@ -31,6 +31,7 @@ from configHandler import idleConf
import idlever import idlever
import rpc import rpc
import Debugger
import RemoteDebugger import RemoteDebugger
IDENTCHARS = string.ascii_letters + string.digits + "_" IDENTCHARS = string.ascii_letters + string.digits + "_"
...@@ -160,9 +161,10 @@ class PyShellEditorWindow(EditorWindow): ...@@ -160,9 +161,10 @@ class PyShellEditorWindow(EditorWindow):
# a temporary file save feature the save breaks functionality # a temporary file save feature the save breaks functionality
# needs to be re-verified, since the breaks at the time the # needs to be re-verified, since the breaks at the time the
# temp file is created may differ from the breaks at the last # temp file is created may differ from the breaks at the last
# permanent save of the file. A break introduced after a save # permanent save of the file. Currently, a break introduced
# will be effective, but not persistent. This is necessary to # after a save will be effective, but not persistent.
# keep the saved breaks synched with the saved file. # This is necessary to keep the saved breaks synched with the
# saved file.
# #
# Breakpoints are set as tagged ranges in the text. Certain # Breakpoints are set as tagged ranges in the text. Certain
# kinds of edits cause these ranges to be deleted: Inserting # kinds of edits cause these ranges to be deleted: Inserting
...@@ -361,17 +363,18 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -361,17 +363,18 @@ class ModifiedInterpreter(InteractiveInterpreter):
# Kill subprocess, spawn a new one, accept connection. # Kill subprocess, spawn a new one, accept connection.
self.rpcclt.close() self.rpcclt.close()
self.unix_terminate() self.unix_terminate()
self.tkconsole.executing = False console = self.tkconsole
console.executing = False
self.spawn_subprocess() self.spawn_subprocess()
self.rpcclt.accept() self.rpcclt.accept()
self.transfer_path() self.transfer_path()
# annotate restart in shell window and mark it # annotate restart in shell window and mark it
console = self.tkconsole
console.text.delete("iomark", "end-1c") console.text.delete("iomark", "end-1c")
halfbar = ((int(console.width) - 16) // 2) * '=' halfbar = ((int(console.width) - 16) // 2) * '='
console.write(halfbar + ' RESTART ' + halfbar) console.write(halfbar + ' RESTART ' + halfbar)
console.text.mark_set("restart", "end-1c") console.text.mark_set("restart", "end-1c")
console.text.mark_gravity("restart", "left") console.text.mark_gravity("restart", "left")
console.showprompt()
# restart subprocess debugger # restart subprocess debugger
if debug: if debug:
# Restarted debugger connects to current instance of debug GUI # Restarted debugger connects to current instance of debug GUI
...@@ -489,8 +492,9 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -489,8 +492,9 @@ class ModifiedInterpreter(InteractiveInterpreter):
code = compile(source, filename, "exec") code = compile(source, filename, "exec")
except (OverflowError, SyntaxError): except (OverflowError, SyntaxError):
self.tkconsole.resetoutput() self.tkconsole.resetoutput()
console = self.tkconsole.console tkerr = self.tkconsole.stderr
print >>console, 'Traceback (most recent call last):' print>>tkerr, '*** Error in script or command!\n'
print>>tkerr, 'Traceback (most recent call last):'
InteractiveInterpreter.showsyntaxerror(self, filename) InteractiveInterpreter.showsyntaxerror(self, filename)
self.tkconsole.showprompt() self.tkconsole.showprompt()
else: else:
...@@ -608,30 +612,34 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -608,30 +612,34 @@ class ModifiedInterpreter(InteractiveInterpreter):
warnings.filters[:] = self.save_warnings_filters warnings.filters[:] = self.save_warnings_filters
self.save_warnings_filters = None self.save_warnings_filters = None
debugger = self.debugger debugger = self.debugger
self.tkconsole.beginexecuting()
try: try:
if not debugger and self.rpcclt is not None: self.tkconsole.beginexecuting()
self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", try:
(code,), {}) if not debugger and self.rpcclt is not None:
elif debugger: self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
debugger.run(code, self.locals) (code,), {})
else: elif debugger:
exec code in self.locals debugger.run(code, self.locals)
except SystemExit: else:
if tkMessageBox.askyesno( exec code in self.locals
"Exit?", except SystemExit:
"Do you want to exit altogether?", if tkMessageBox.askyesno(
default="yes", "Exit?",
master=self.tkconsole.text): "Do you want to exit altogether?",
raise default="yes",
else: master=self.tkconsole.text):
raise
else:
self.showtraceback()
except:
self.showtraceback() self.showtraceback()
except: finally:
self.showtraceback() if not use_subprocess:
self.tkconsole.endexecuting()
def write(self, s): def write(self, s):
"Override base class method" "Override base class method"
self.tkconsole.console.write(s) self.tkconsole.stderr.write(s)
class PyShell(OutputWindow): class PyShell(OutputWindow):
...@@ -741,22 +749,13 @@ class PyShell(OutputWindow): ...@@ -741,22 +749,13 @@ class PyShell(OutputWindow):
self.set_debugger_indicator() self.set_debugger_indicator()
def open_debugger(self): 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: if self.interp.rpcclt:
return self.open_remote_debugger() dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
import Debugger self)
self.interp.setdebugger(Debugger.Debugger(self)) else:
sys.ps1 = "[DEBUG ON]\n>>> " dbg_gui = Debugger.Debugger(self)
self.showprompt() self.interp.setdebugger(dbg_gui)
self.set_debugger_indicator() dbg_gui.load_breakpoints()
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()
sys.ps1 = "[DEBUG ON]\n>>> " sys.ps1 = "[DEBUG ON]\n>>> "
self.showprompt() self.showprompt()
self.set_debugger_indicator() self.set_debugger_indicator()
...@@ -779,18 +778,22 @@ class PyShell(OutputWindow): ...@@ -779,18 +778,22 @@ class PyShell(OutputWindow):
"Kill?", "Kill?",
"The program is still running!\n Do you want to kill it?", "The program is still running!\n Do you want to kill it?",
default="ok", default="ok",
master=self.text) parent=self.text)
if response == False: if response == False:
return "cancel" return "cancel"
# interrupt the subprocess # interrupt the subprocess
self.closing = True self.canceled = True
self.endexecuting() if use_subprocess:
return EditorWindow.close(self) self.interp.interrupt_subprocess()
return "cancel"
else:
return EditorWindow.close(self)
def _close(self): def _close(self):
"Extend EditorWindow._close(), shut down debugger and execution server" "Extend EditorWindow._close(), shut down debugger and execution server"
self.close_debugger() self.close_debugger()
self.interp.kill_subprocess() if use_subprocess:
self.interp.kill_subprocess()
# Restore std streams # Restore std streams
sys.stdout = self.save_stdout sys.stdout = self.save_stdout
sys.stderr = self.save_stderr sys.stderr = self.save_stderr
...@@ -814,9 +817,13 @@ class PyShell(OutputWindow): ...@@ -814,9 +817,13 @@ class PyShell(OutputWindow):
def begin(self): def begin(self):
self.resetoutput() 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, (sys.version, sys.platform, self.COPYRIGHT,
idlever.IDLE_VERSION)) idlever.IDLE_VERSION, nosub))
self.showprompt() self.showprompt()
import Tkinter import Tkinter
Tkinter._default_root = None Tkinter._default_root = None
...@@ -853,7 +860,7 @@ class PyShell(OutputWindow): ...@@ -853,7 +860,7 @@ class PyShell(OutputWindow):
pass pass
if not (self.executing or self.reading): if not (self.executing or self.reading):
self.resetoutput() self.resetoutput()
self.write("KeyboardInterrupt\n") self.interp.write("KeyboardInterrupt\n")
self.showprompt() self.showprompt()
return "break" return "break"
self.endoffile = 0 self.endoffile = 0
...@@ -997,8 +1004,16 @@ class PyShell(OutputWindow): ...@@ -997,8 +1004,16 @@ class PyShell(OutputWindow):
self.text.see("restart") self.text.see("restart")
def restart_shell(self, event=None): def restart_shell(self, event=None):
self.interp.restart_subprocess() if self.executing:
self.showprompt() 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): def showprompt(self):
self.resetoutput() self.resetoutput()
...@@ -1030,6 +1045,8 @@ class PyShell(OutputWindow): ...@@ -1030,6 +1045,8 @@ class PyShell(OutputWindow):
pass pass
if self.canceled: if self.canceled:
self.canceled = 0 self.canceled = 0
if not use_subprocess:
raise KeyboardInterrupt
class PseudoFile: class PseudoFile:
......
...@@ -22,6 +22,7 @@ import string ...@@ -22,6 +22,7 @@ import string
import tabnanny import tabnanny
import tokenize import tokenize
import tkMessageBox import tkMessageBox
import PyShell
IDENTCHARS = string.ascii_letters + string.digits + "_" IDENTCHARS = string.ascii_letters + string.digits + "_"
...@@ -38,8 +39,6 @@ To fix case 2, change all tabs to spaces by using Select All followed \ ...@@ -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).""" by Untabify Region (both in the Edit menu)."""
# XXX 11Jun02 KBK TBD Implement stop-execution
class ScriptBinding: class ScriptBinding:
menudefs = [ menudefs = [
...@@ -124,7 +123,19 @@ class ScriptBinding: ...@@ -124,7 +123,19 @@ class ScriptBinding:
flist = self.editwin.flist flist = self.editwin.flist
shell = flist.open_shell() shell = flist.open_shell()
interp = shell.interp 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... # XXX Too often this discards arguments the user just set...
interp.runcommand("""if 1: interp.runcommand("""if 1:
_filename = %s _filename = %s
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment