cpython/Tools/idle/ScriptBinding.py

155 lines
4.8 KiB
Python

"""Extension to execute code outside the Python shell window.
This adds two commands (to the Edit menu, until there's a separate
Python menu):
- Run module (F5) is equivalent to either import or reload of the
current module. The window must have been saved previously. The
module only gets added to sys.modules, it doesn't get added to
anyone's namespace; you can import it in the shell if you need to. If
this generates any output to sys.stdout or sys.stderr, a new output
window is created to display that output. The two streams are
distinguished by their text color.
- Debug module (Control-F5) does the same but executes the module's
code in the debugger.
When an unhandled exception occurs in Run module, the stack viewer is
popped up. This is not done in Debug module, because you've already
had an opportunity to view the stack. In either case, the variables
sys.last_type, sys.last_value, sys.last_traceback are set to the
exception info.
"""
import sys
import os
import imp
import linecache
import traceback
import tkMessageBox
from OutputWindow import OutputWindow
# XXX These helper classes are more generally usable!
class OnDemandOutputWindow:
tagdefs = {
"stdout": {"foreground": "blue"},
"stderr": {"foreground": "#007700"},
}
def __init__(self, flist):
self.flist = flist
self.owin = None
def write(self, s, tags, mark):
if not self.owin:
self.setup()
self.owin.write(s, tags, mark)
def setup(self):
self.owin = owin = OutputWindow(self.flist)
text = owin.text
for tag, cnf in self.tagdefs.items():
if cnf:
apply(text.tag_configure, (tag,), cnf)
text.tag_raise('sel')
self.write = self.owin.write
class PseudoFile:
def __init__(self, owin, tags, mark="end"):
self.owin = owin
self.tags = tags
self.mark = mark
def write(self, s):
self.owin.write(s, self.tags, self.mark)
def writelines(self, l):
map(self.write, l)
def flush(self):
pass
class ScriptBinding:
keydefs = {
'<<run-module>>': ['<F5>'],
'<<debug-module>>': ['<Control-F5>'],
}
menudefs = [
('edit', [None,
('Run module', '<<run-module>>'),
('Debug module', '<<debug-module>>'),
]
),
]
def __init__(self, editwin):
self.editwin = editwin
# Provide instance variables referenced by Debugger
# XXX This should be done differently
self.flist = self.editwin.flist
self.root = self.flist.root
def run_module_event(self, event, debugger=None):
if not self.editwin.get_saved():
tkMessageBox.showerror("Not saved",
"Please save first!",
master=self.editwin.text)
self.editwin.text.focus_set()
return
filename = self.editwin.io.filename
if not filename:
tkMessageBox.showerror("No file name",
"This window has no file name",
master=self.editwin.text)
self.editwin.text.focus_set()
return
modname, ext = os.path.splitext(os.path.basename(filename))
if sys.modules.has_key(modname):
mod = sys.modules[modname]
else:
mod = imp.new_module(modname)
sys.modules[modname] = mod
mod.__file__ = filename
saveout = sys.stdout
saveerr = sys.stderr
owin = OnDemandOutputWindow(self.editwin.flist)
try:
sys.stderr = PseudoFile(owin, "stderr")
try:
sys.stdout = PseudoFile(owin, "stdout")
try:
if debugger:
debugger.run("execfile(%s)" % `filename`, mod.__dict__)
else:
execfile(filename, mod.__dict__)
except:
(sys.last_type, sys.last_value,
sys.last_traceback) = sys.exc_info()
linecache.checkcache()
traceback.print_exc()
if not debugger and \
self.editwin.getvar("<<toggle-jit-stack-viewer>>"):
from StackViewer import StackBrowser
sv = StackBrowser(self.root, self.flist)
finally:
sys.stdout = saveout
finally:
sys.stderr = saveerr
def debug_module_event(self, event):
import Debugger
debugger = Debugger.Debugger(self)
self.run_module_event(event, debugger)
def close_debugger(self):
# Method called by Debugger
# XXX This should be done differently
pass