Require implementations for warnings.showwarning() support the 'line' argument.
[python.git] / Lib / idlelib / PyShell.py
blob7fec7e992fbb9d629cd29b772a8cd58e2d962a62
1 #! /usr/bin/env python
3 import os
4 import os.path
5 import sys
6 import string
7 import getopt
8 import re
9 import socket
10 import time
11 import threading
12 import traceback
13 import types
14 import macosxSupport
16 import linecache
17 from code import InteractiveInterpreter
19 try:
20 from Tkinter import *
21 except ImportError:
22 print>>sys.__stderr__, "** IDLE can't import Tkinter. " \
23 "Your Python may not be configured for Tk. **"
24 sys.exit(1)
25 import tkMessageBox
27 from EditorWindow import EditorWindow, fixwordbreaks
28 from FileList import FileList
29 from ColorDelegator import ColorDelegator
30 from UndoDelegator import UndoDelegator
31 from OutputWindow import OutputWindow
32 from configHandler import idleConf
33 import idlever
35 import rpc
36 import Debugger
37 import RemoteDebugger
39 IDENTCHARS = string.ascii_letters + string.digits + "_"
40 LOCALHOST = '127.0.0.1'
42 try:
43 from signal import SIGTERM
44 except ImportError:
45 SIGTERM = 15
47 # Override warnings module to write to warning_stream. Initialize to send IDLE
48 # internal warnings to the console. ScriptBinding.check_syntax() will
49 # temporarily redirect the stream to the shell window to display warnings when
50 # checking user's code.
51 global warning_stream
52 warning_stream = sys.__stderr__
53 try:
54 import warnings
55 except ImportError:
56 pass
57 else:
58 def idle_showwarning(message, category, filename, lineno,
59 file=None, line=None):
60 file = warning_stream
61 try:
62 file.write(warnings.formatwarning(message, category, filename,\
63 lineno, file=file, line=line))
64 except IOError:
65 pass ## file (probably __stderr__) is invalid, warning dropped.
66 warnings.showwarning = idle_showwarning
67 def idle_formatwarning(message, category, filename, lineno,
68 file=None, line=None):
69 """Format warnings the IDLE way"""
70 s = "\nWarning (from warnings module):\n"
71 s += ' File \"%s\", line %s\n' % (filename, lineno)
72 line = linecache.getline(filename, lineno).strip() \
73 if line is None else line
74 if line:
75 s += " %s\n" % line
76 s += "%s: %s\n>>> " % (category.__name__, message)
77 return s
78 warnings.formatwarning = idle_formatwarning
80 def extended_linecache_checkcache(filename=None,
81 orig_checkcache=linecache.checkcache):
82 """Extend linecache.checkcache to preserve the <pyshell#...> entries
84 Rather than repeating the linecache code, patch it to save the
85 <pyshell#...> entries, call the original linecache.checkcache()
86 (which destroys them), and then restore the saved entries.
88 orig_checkcache is bound at definition time to the original
89 method, allowing it to be patched.
91 """
92 cache = linecache.cache
93 save = {}
94 for filename in cache.keys():
95 if filename[:1] + filename[-1:] == '<>':
96 save[filename] = cache[filename]
97 orig_checkcache()
98 cache.update(save)
100 # Patch linecache.checkcache():
101 linecache.checkcache = extended_linecache_checkcache
104 class PyShellEditorWindow(EditorWindow):
105 "Regular text edit window in IDLE, supports breakpoints"
107 def __init__(self, *args):
108 self.breakpoints = []
109 EditorWindow.__init__(self, *args)
110 self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
111 self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
112 self.text.bind("<<open-python-shell>>", self.flist.open_shell)
114 self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
115 'breakpoints.lst')
116 # whenever a file is changed, restore breakpoints
117 if self.io.filename: self.restore_file_breaks()
118 def filename_changed_hook(old_hook=self.io.filename_change_hook,
119 self=self):
120 self.restore_file_breaks()
121 old_hook()
122 self.io.set_filename_change_hook(filename_changed_hook)
124 rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),
125 ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
127 def set_breakpoint(self, lineno):
128 text = self.text
129 filename = self.io.filename
130 text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
131 try:
132 i = self.breakpoints.index(lineno)
133 except ValueError: # only add if missing, i.e. do once
134 self.breakpoints.append(lineno)
135 try: # update the subprocess debugger
136 debug = self.flist.pyshell.interp.debugger
137 debug.set_breakpoint_here(filename, lineno)
138 except: # but debugger may not be active right now....
139 pass
141 def set_breakpoint_here(self, event=None):
142 text = self.text
143 filename = self.io.filename
144 if not filename:
145 text.bell()
146 return
147 lineno = int(float(text.index("insert")))
148 self.set_breakpoint(lineno)
150 def clear_breakpoint_here(self, event=None):
151 text = self.text
152 filename = self.io.filename
153 if not filename:
154 text.bell()
155 return
156 lineno = int(float(text.index("insert")))
157 try:
158 self.breakpoints.remove(lineno)
159 except:
160 pass
161 text.tag_remove("BREAK", "insert linestart",\
162 "insert lineend +1char")
163 try:
164 debug = self.flist.pyshell.interp.debugger
165 debug.clear_breakpoint_here(filename, lineno)
166 except:
167 pass
169 def clear_file_breaks(self):
170 if self.breakpoints:
171 text = self.text
172 filename = self.io.filename
173 if not filename:
174 text.bell()
175 return
176 self.breakpoints = []
177 text.tag_remove("BREAK", "1.0", END)
178 try:
179 debug = self.flist.pyshell.interp.debugger
180 debug.clear_file_breaks(filename)
181 except:
182 pass
184 def store_file_breaks(self):
185 "Save breakpoints when file is saved"
186 # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
187 # be run. The breaks are saved at that time. If we introduce
188 # a temporary file save feature the save breaks functionality
189 # needs to be re-verified, since the breaks at the time the
190 # temp file is created may differ from the breaks at the last
191 # permanent save of the file. Currently, a break introduced
192 # after a save will be effective, but not persistent.
193 # This is necessary to keep the saved breaks synched with the
194 # saved file.
196 # Breakpoints are set as tagged ranges in the text. Certain
197 # kinds of edits cause these ranges to be deleted: Inserting
198 # or deleting a line just before a breakpoint, and certain
199 # deletions prior to a breakpoint. These issues need to be
200 # investigated and understood. It's not clear if they are
201 # Tk issues or IDLE issues, or whether they can actually
202 # be fixed. Since a modified file has to be saved before it is
203 # run, and since self.breakpoints (from which the subprocess
204 # debugger is loaded) is updated during the save, the visible
205 # breaks stay synched with the subprocess even if one of these
206 # unexpected breakpoint deletions occurs.
207 breaks = self.breakpoints
208 filename = self.io.filename
209 try:
210 lines = open(self.breakpointPath,"r").readlines()
211 except IOError:
212 lines = []
213 new_file = open(self.breakpointPath,"w")
214 for line in lines:
215 if not line.startswith(filename + '='):
216 new_file.write(line)
217 self.update_breakpoints()
218 breaks = self.breakpoints
219 if breaks:
220 new_file.write(filename + '=' + str(breaks) + '\n')
221 new_file.close()
223 def restore_file_breaks(self):
224 self.text.update() # this enables setting "BREAK" tags to be visible
225 filename = self.io.filename
226 if filename is None:
227 return
228 if os.path.isfile(self.breakpointPath):
229 lines = open(self.breakpointPath,"r").readlines()
230 for line in lines:
231 if line.startswith(filename + '='):
232 breakpoint_linenumbers = eval(line[len(filename)+1:])
233 for breakpoint_linenumber in breakpoint_linenumbers:
234 self.set_breakpoint(breakpoint_linenumber)
236 def update_breakpoints(self):
237 "Retrieves all the breakpoints in the current window"
238 text = self.text
239 ranges = text.tag_ranges("BREAK")
240 linenumber_list = self.ranges_to_linenumbers(ranges)
241 self.breakpoints = linenumber_list
243 def ranges_to_linenumbers(self, ranges):
244 lines = []
245 for index in range(0, len(ranges), 2):
246 lineno = int(float(ranges[index]))
247 end = int(float(ranges[index+1]))
248 while lineno < end:
249 lines.append(lineno)
250 lineno += 1
251 return lines
253 # XXX 13 Dec 2002 KBK Not used currently
254 # def saved_change_hook(self):
255 # "Extend base method - clear breaks if module is modified"
256 # if not self.get_saved():
257 # self.clear_file_breaks()
258 # EditorWindow.saved_change_hook(self)
260 def _close(self):
261 "Extend base method - clear breaks when module is closed"
262 self.clear_file_breaks()
263 EditorWindow._close(self)
266 class PyShellFileList(FileList):
267 "Extend base class: IDLE supports a shell and breakpoints"
269 # override FileList's class variable, instances return PyShellEditorWindow
270 # instead of EditorWindow when new edit windows are created.
271 EditorWindow = PyShellEditorWindow
273 pyshell = None
275 def open_shell(self, event=None):
276 if self.pyshell:
277 self.pyshell.top.wakeup()
278 else:
279 self.pyshell = PyShell(self)
280 if self.pyshell:
281 if not self.pyshell.begin():
282 return None
283 return self.pyshell
286 class ModifiedColorDelegator(ColorDelegator):
287 "Extend base class: colorizer for the shell window itself"
289 def __init__(self):
290 ColorDelegator.__init__(self)
291 self.LoadTagDefs()
293 def recolorize_main(self):
294 self.tag_remove("TODO", "1.0", "iomark")
295 self.tag_add("SYNC", "1.0", "iomark")
296 ColorDelegator.recolorize_main(self)
298 def LoadTagDefs(self):
299 ColorDelegator.LoadTagDefs(self)
300 theme = idleConf.GetOption('main','Theme','name')
301 self.tagdefs.update({
302 "stdin": {'background':None,'foreground':None},
303 "stdout": idleConf.GetHighlight(theme, "stdout"),
304 "stderr": idleConf.GetHighlight(theme, "stderr"),
305 "console": idleConf.GetHighlight(theme, "console"),
308 class ModifiedUndoDelegator(UndoDelegator):
309 "Extend base class: forbid insert/delete before the I/O mark"
311 def insert(self, index, chars, tags=None):
312 try:
313 if self.delegate.compare(index, "<", "iomark"):
314 self.delegate.bell()
315 return
316 except TclError:
317 pass
318 UndoDelegator.insert(self, index, chars, tags)
320 def delete(self, index1, index2=None):
321 try:
322 if self.delegate.compare(index1, "<", "iomark"):
323 self.delegate.bell()
324 return
325 except TclError:
326 pass
327 UndoDelegator.delete(self, index1, index2)
330 class MyRPCClient(rpc.RPCClient):
332 def handle_EOF(self):
333 "Override the base class - just re-raise EOFError"
334 raise EOFError
337 class ModifiedInterpreter(InteractiveInterpreter):
339 def __init__(self, tkconsole):
340 self.tkconsole = tkconsole
341 locals = sys.modules['__main__'].__dict__
342 InteractiveInterpreter.__init__(self, locals=locals)
343 self.save_warnings_filters = None
344 self.restarting = False
345 self.subprocess_arglist = self.build_subprocess_arglist()
347 port = 8833
348 rpcclt = None
349 rpcpid = None
351 def spawn_subprocess(self):
352 args = self.subprocess_arglist
353 self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args)
355 def build_subprocess_arglist(self):
356 w = ['-W' + s for s in sys.warnoptions]
357 if 1/2 > 0: # account for new division
358 w.append('-Qnew')
359 # Maybe IDLE is installed and is being accessed via sys.path,
360 # or maybe it's not installed and the idle.py script is being
361 # run from the IDLE source directory.
362 del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
363 default=False, type='bool')
364 if __name__ == 'idlelib.PyShell':
365 command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
366 else:
367 command = "__import__('run').main(%r)" % (del_exitf,)
368 if sys.platform[:3] == 'win' and ' ' in sys.executable:
369 # handle embedded space in path by quoting the argument
370 decorated_exec = '"%s"' % sys.executable
371 else:
372 decorated_exec = sys.executable
373 return [decorated_exec] + w + ["-c", command, str(self.port)]
375 def start_subprocess(self):
376 # spawning first avoids passing a listening socket to the subprocess
377 self.spawn_subprocess()
378 #time.sleep(20) # test to simulate GUI not accepting connection
379 addr = (LOCALHOST, self.port)
380 # Idle starts listening for connection on localhost
381 for i in range(3):
382 time.sleep(i)
383 try:
384 self.rpcclt = MyRPCClient(addr)
385 break
386 except socket.error, err:
387 pass
388 else:
389 self.display_port_binding_error()
390 return None
391 # Accept the connection from the Python execution server
392 self.rpcclt.listening_sock.settimeout(10)
393 try:
394 self.rpcclt.accept()
395 except socket.timeout, err:
396 self.display_no_subprocess_error()
397 return None
398 self.rpcclt.register("stdin", self.tkconsole)
399 self.rpcclt.register("stdout", self.tkconsole.stdout)
400 self.rpcclt.register("stderr", self.tkconsole.stderr)
401 self.rpcclt.register("flist", self.tkconsole.flist)
402 self.rpcclt.register("linecache", linecache)
403 self.rpcclt.register("interp", self)
404 self.transfer_path()
405 self.poll_subprocess()
406 return self.rpcclt
408 def restart_subprocess(self):
409 if self.restarting:
410 return self.rpcclt
411 self.restarting = True
412 # close only the subprocess debugger
413 debug = self.getdebugger()
414 if debug:
415 try:
416 # Only close subprocess debugger, don't unregister gui_adap!
417 RemoteDebugger.close_subprocess_debugger(self.rpcclt)
418 except:
419 pass
420 # Kill subprocess, spawn a new one, accept connection.
421 self.rpcclt.close()
422 self.unix_terminate()
423 console = self.tkconsole
424 was_executing = console.executing
425 console.executing = False
426 self.spawn_subprocess()
427 try:
428 self.rpcclt.accept()
429 except socket.timeout, err:
430 self.display_no_subprocess_error()
431 return None
432 self.transfer_path()
433 # annotate restart in shell window and mark it
434 console.text.delete("iomark", "end-1c")
435 if was_executing:
436 console.write('\n')
437 console.showprompt()
438 halfbar = ((int(console.width) - 16) // 2) * '='
439 console.write(halfbar + ' RESTART ' + halfbar)
440 console.text.mark_set("restart", "end-1c")
441 console.text.mark_gravity("restart", "left")
442 console.showprompt()
443 # restart subprocess debugger
444 if debug:
445 # Restarted debugger connects to current instance of debug GUI
446 gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
447 # reload remote debugger breakpoints for all PyShellEditWindows
448 debug.load_breakpoints()
449 self.restarting = False
450 return self.rpcclt
452 def __request_interrupt(self):
453 self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
455 def interrupt_subprocess(self):
456 threading.Thread(target=self.__request_interrupt).start()
458 def kill_subprocess(self):
459 try:
460 self.rpcclt.close()
461 except AttributeError: # no socket
462 pass
463 self.unix_terminate()
464 self.tkconsole.executing = False
465 self.rpcclt = None
467 def unix_terminate(self):
468 "UNIX: make sure subprocess is terminated and collect status"
469 if hasattr(os, 'kill'):
470 try:
471 os.kill(self.rpcpid, SIGTERM)
472 except OSError:
473 # process already terminated:
474 return
475 else:
476 try:
477 os.waitpid(self.rpcpid, 0)
478 except OSError:
479 return
481 def transfer_path(self):
482 self.runcommand("""if 1:
483 import sys as _sys
484 _sys.path = %r
485 del _sys
486 \n""" % (sys.path,))
488 active_seq = None
490 def poll_subprocess(self):
491 clt = self.rpcclt
492 if clt is None:
493 return
494 try:
495 response = clt.pollresponse(self.active_seq, wait=0.05)
496 except (EOFError, IOError, KeyboardInterrupt):
497 # lost connection or subprocess terminated itself, restart
498 # [the KBI is from rpc.SocketIO.handle_EOF()]
499 if self.tkconsole.closing:
500 return
501 response = None
502 self.restart_subprocess()
503 if response:
504 self.tkconsole.resetoutput()
505 self.active_seq = None
506 how, what = response
507 console = self.tkconsole.console
508 if how == "OK":
509 if what is not None:
510 print >>console, repr(what)
511 elif how == "EXCEPTION":
512 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
513 self.remote_stack_viewer()
514 elif how == "ERROR":
515 errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
516 print >>sys.__stderr__, errmsg, what
517 print >>console, errmsg, what
518 # we received a response to the currently active seq number:
519 try:
520 self.tkconsole.endexecuting()
521 except AttributeError: # shell may have closed
522 pass
523 # Reschedule myself
524 if not self.tkconsole.closing:
525 self.tkconsole.text.after(self.tkconsole.pollinterval,
526 self.poll_subprocess)
528 debugger = None
530 def setdebugger(self, debugger):
531 self.debugger = debugger
533 def getdebugger(self):
534 return self.debugger
536 def open_remote_stack_viewer(self):
537 """Initiate the remote stack viewer from a separate thread.
539 This method is called from the subprocess, and by returning from this
540 method we allow the subprocess to unblock. After a bit the shell
541 requests the subprocess to open the remote stack viewer which returns a
542 static object looking at the last exceptiopn. It is queried through
543 the RPC mechanism.
546 self.tkconsole.text.after(300, self.remote_stack_viewer)
547 return
549 def remote_stack_viewer(self):
550 import RemoteObjectBrowser
551 oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
552 if oid is None:
553 self.tkconsole.root.bell()
554 return
555 item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid)
556 from TreeWidget import ScrolledCanvas, TreeNode
557 top = Toplevel(self.tkconsole.root)
558 theme = idleConf.GetOption('main','Theme','name')
559 background = idleConf.GetHighlight(theme, 'normal')['background']
560 sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
561 sc.frame.pack(expand=1, fill="both")
562 node = TreeNode(sc.canvas, None, item)
563 node.expand()
564 # XXX Should GC the remote tree when closing the window
566 gid = 0
568 def execsource(self, source):
569 "Like runsource() but assumes complete exec source"
570 filename = self.stuffsource(source)
571 self.execfile(filename, source)
573 def execfile(self, filename, source=None):
574 "Execute an existing file"
575 if source is None:
576 source = open(filename, "r").read()
577 try:
578 code = compile(source, filename, "exec")
579 except (OverflowError, SyntaxError):
580 self.tkconsole.resetoutput()
581 tkerr = self.tkconsole.stderr
582 print>>tkerr, '*** Error in script or command!\n'
583 print>>tkerr, 'Traceback (most recent call last):'
584 InteractiveInterpreter.showsyntaxerror(self, filename)
585 self.tkconsole.showprompt()
586 else:
587 self.runcode(code)
589 def runsource(self, source):
590 "Extend base class method: Stuff the source in the line cache first"
591 filename = self.stuffsource(source)
592 self.more = 0
593 self.save_warnings_filters = warnings.filters[:]
594 warnings.filterwarnings(action="error", category=SyntaxWarning)
595 if isinstance(source, types.UnicodeType):
596 import IOBinding
597 try:
598 source = source.encode(IOBinding.encoding)
599 except UnicodeError:
600 self.tkconsole.resetoutput()
601 self.write("Unsupported characters in input\n")
602 return
603 try:
604 # InteractiveInterpreter.runsource() calls its runcode() method,
605 # which is overridden (see below)
606 return InteractiveInterpreter.runsource(self, source, filename)
607 finally:
608 if self.save_warnings_filters is not None:
609 warnings.filters[:] = self.save_warnings_filters
610 self.save_warnings_filters = None
612 def stuffsource(self, source):
613 "Stuff source in the filename cache"
614 filename = "<pyshell#%d>" % self.gid
615 self.gid = self.gid + 1
616 lines = source.split("\n")
617 linecache.cache[filename] = len(source)+1, 0, lines, filename
618 return filename
620 def prepend_syspath(self, filename):
621 "Prepend sys.path with file's directory if not already included"
622 self.runcommand("""if 1:
623 _filename = %r
624 import sys as _sys
625 from os.path import dirname as _dirname
626 _dir = _dirname(_filename)
627 if not _dir in _sys.path:
628 _sys.path.insert(0, _dir)
629 del _filename, _sys, _dirname, _dir
630 \n""" % (filename,))
632 def showsyntaxerror(self, filename=None):
633 """Extend base class method: Add Colorizing
635 Color the offending position instead of printing it and pointing at it
636 with a caret.
639 text = self.tkconsole.text
640 stuff = self.unpackerror()
641 if stuff:
642 msg, lineno, offset, line = stuff
643 if lineno == 1:
644 pos = "iomark + %d chars" % (offset-1)
645 else:
646 pos = "iomark linestart + %d lines + %d chars" % \
647 (lineno-1, offset-1)
648 text.tag_add("ERROR", pos)
649 text.see(pos)
650 char = text.get(pos)
651 if char and char in IDENTCHARS:
652 text.tag_add("ERROR", pos + " wordstart", pos)
653 self.tkconsole.resetoutput()
654 self.write("SyntaxError: %s\n" % str(msg))
655 else:
656 self.tkconsole.resetoutput()
657 InteractiveInterpreter.showsyntaxerror(self, filename)
658 self.tkconsole.showprompt()
660 def unpackerror(self):
661 type, value, tb = sys.exc_info()
662 ok = type is SyntaxError
663 if ok:
664 try:
665 msg, (dummy_filename, lineno, offset, line) = value
666 if not offset:
667 offset = 0
668 except:
669 ok = 0
670 if ok:
671 return msg, lineno, offset, line
672 else:
673 return None
675 def showtraceback(self):
676 "Extend base class method to reset output properly"
677 self.tkconsole.resetoutput()
678 self.checklinecache()
679 InteractiveInterpreter.showtraceback(self)
680 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
681 self.tkconsole.open_stack_viewer()
683 def checklinecache(self):
684 c = linecache.cache
685 for key in c.keys():
686 if key[:1] + key[-1:] != "<>":
687 del c[key]
689 def runcommand(self, code):
690 "Run the code without invoking the debugger"
691 # The code better not raise an exception!
692 if self.tkconsole.executing:
693 self.display_executing_dialog()
694 return 0
695 if self.rpcclt:
696 self.rpcclt.remotequeue("exec", "runcode", (code,), {})
697 else:
698 exec code in self.locals
699 return 1
701 def runcode(self, code):
702 "Override base class method"
703 if self.tkconsole.executing:
704 self.interp.restart_subprocess()
705 self.checklinecache()
706 if self.save_warnings_filters is not None:
707 warnings.filters[:] = self.save_warnings_filters
708 self.save_warnings_filters = None
709 debugger = self.debugger
710 try:
711 self.tkconsole.beginexecuting()
712 if not debugger and self.rpcclt is not None:
713 self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
714 (code,), {})
715 elif debugger:
716 debugger.run(code, self.locals)
717 else:
718 exec code in self.locals
719 except SystemExit:
720 if not self.tkconsole.closing:
721 if tkMessageBox.askyesno(
722 "Exit?",
723 "Do you want to exit altogether?",
724 default="yes",
725 master=self.tkconsole.text):
726 raise
727 else:
728 self.showtraceback()
729 else:
730 raise
731 except:
732 if use_subprocess:
733 print >>self.tkconsole.stderr, \
734 "IDLE internal error in runcode()"
735 self.showtraceback()
736 self.tkconsole.endexecuting()
737 else:
738 if self.tkconsole.canceled:
739 self.tkconsole.canceled = False
740 print >>self.tkconsole.stderr, "KeyboardInterrupt"
741 else:
742 self.showtraceback()
743 finally:
744 if not use_subprocess:
745 try:
746 self.tkconsole.endexecuting()
747 except AttributeError: # shell may have closed
748 pass
750 def write(self, s):
751 "Override base class method"
752 self.tkconsole.stderr.write(s)
754 def display_port_binding_error(self):
755 tkMessageBox.showerror(
756 "Port Binding Error",
757 "IDLE can't bind TCP/IP port 8833, which is necessary to "
758 "communicate with its Python execution server. Either "
759 "no networking is installed on this computer or another "
760 "process (another IDLE?) is using the port. Run IDLE with the -n "
761 "command line switch to start without a subprocess and refer to "
762 "Help/IDLE Help 'Running without a subprocess' for further "
763 "details.",
764 master=self.tkconsole.text)
766 def display_no_subprocess_error(self):
767 tkMessageBox.showerror(
768 "Subprocess Startup Error",
769 "IDLE's subprocess didn't make connection. Either IDLE can't "
770 "start a subprocess or personal firewall software is blocking "
771 "the connection.",
772 master=self.tkconsole.text)
774 def display_executing_dialog(self):
775 tkMessageBox.showerror(
776 "Already executing",
777 "The Python Shell window is already executing a command; "
778 "please wait until it is finished.",
779 master=self.tkconsole.text)
782 class PyShell(OutputWindow):
784 shell_title = "Python Shell"
786 # Override classes
787 ColorDelegator = ModifiedColorDelegator
788 UndoDelegator = ModifiedUndoDelegator
790 # Override menus
791 menu_specs = [
792 ("file", "_File"),
793 ("edit", "_Edit"),
794 ("debug", "_Debug"),
795 ("options", "_Options"),
796 ("windows", "_Windows"),
797 ("help", "_Help"),
800 if macosxSupport.runningAsOSXApp():
801 del menu_specs[-3]
802 menu_specs[-2] = ("windows", "_Window")
805 # New classes
806 from IdleHistory import History
808 def __init__(self, flist=None):
809 if use_subprocess:
810 ms = self.menu_specs
811 if ms[2][0] != "shell":
812 ms.insert(2, ("shell", "She_ll"))
813 self.interp = ModifiedInterpreter(self)
814 if flist is None:
815 root = Tk()
816 fixwordbreaks(root)
817 root.withdraw()
818 flist = PyShellFileList(root)
820 OutputWindow.__init__(self, flist, None, None)
822 ## self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
823 self.usetabs = True
824 # indentwidth must be 8 when using tabs. See note in EditorWindow:
825 self.indentwidth = 8
826 self.context_use_ps1 = True
828 text = self.text
829 text.configure(wrap="char")
830 text.bind("<<newline-and-indent>>", self.enter_callback)
831 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
832 text.bind("<<interrupt-execution>>", self.cancel_callback)
833 text.bind("<<end-of-file>>", self.eof_callback)
834 text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
835 text.bind("<<toggle-debugger>>", self.toggle_debugger)
836 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
837 if use_subprocess:
838 text.bind("<<view-restart>>", self.view_restart_mark)
839 text.bind("<<restart-shell>>", self.restart_shell)
841 self.save_stdout = sys.stdout
842 self.save_stderr = sys.stderr
843 self.save_stdin = sys.stdin
844 import IOBinding
845 self.stdout = PseudoFile(self, "stdout", IOBinding.encoding)
846 self.stderr = PseudoFile(self, "stderr", IOBinding.encoding)
847 self.console = PseudoFile(self, "console", IOBinding.encoding)
848 if not use_subprocess:
849 sys.stdout = self.stdout
850 sys.stderr = self.stderr
851 sys.stdin = self
853 self.history = self.History(self.text)
855 self.pollinterval = 50 # millisec
857 def get_standard_extension_names(self):
858 return idleConf.GetExtensions(shell_only=True)
860 reading = False
861 executing = False
862 canceled = False
863 endoffile = False
864 closing = False
866 def set_warning_stream(self, stream):
867 global warning_stream
868 warning_stream = stream
870 def get_warning_stream(self):
871 return warning_stream
873 def toggle_debugger(self, event=None):
874 if self.executing:
875 tkMessageBox.showerror("Don't debug now",
876 "You can only toggle the debugger when idle",
877 master=self.text)
878 self.set_debugger_indicator()
879 return "break"
880 else:
881 db = self.interp.getdebugger()
882 if db:
883 self.close_debugger()
884 else:
885 self.open_debugger()
887 def set_debugger_indicator(self):
888 db = self.interp.getdebugger()
889 self.setvar("<<toggle-debugger>>", not not db)
891 def toggle_jit_stack_viewer(self, event=None):
892 pass # All we need is the variable
894 def close_debugger(self):
895 db = self.interp.getdebugger()
896 if db:
897 self.interp.setdebugger(None)
898 db.close()
899 if self.interp.rpcclt:
900 RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
901 self.resetoutput()
902 self.console.write("[DEBUG OFF]\n")
903 sys.ps1 = ">>> "
904 self.showprompt()
905 self.set_debugger_indicator()
907 def open_debugger(self):
908 if self.interp.rpcclt:
909 dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
910 self)
911 else:
912 dbg_gui = Debugger.Debugger(self)
913 self.interp.setdebugger(dbg_gui)
914 dbg_gui.load_breakpoints()
915 sys.ps1 = "[DEBUG ON]\n>>> "
916 self.showprompt()
917 self.set_debugger_indicator()
919 def beginexecuting(self):
920 "Helper for ModifiedInterpreter"
921 self.resetoutput()
922 self.executing = 1
924 def endexecuting(self):
925 "Helper for ModifiedInterpreter"
926 self.executing = 0
927 self.canceled = 0
928 self.showprompt()
930 def close(self):
931 "Extend EditorWindow.close()"
932 if self.executing:
933 response = tkMessageBox.askokcancel(
934 "Kill?",
935 "The program is still running!\n Do you want to kill it?",
936 default="ok",
937 parent=self.text)
938 if response is False:
939 return "cancel"
940 if self.reading:
941 self.top.quit()
942 self.canceled = True
943 self.closing = True
944 # Wait for poll_subprocess() rescheduling to stop
945 self.text.after(2 * self.pollinterval, self.close2)
947 def close2(self):
948 return EditorWindow.close(self)
950 def _close(self):
951 "Extend EditorWindow._close(), shut down debugger and execution server"
952 self.close_debugger()
953 if use_subprocess:
954 self.interp.kill_subprocess()
955 # Restore std streams
956 sys.stdout = self.save_stdout
957 sys.stderr = self.save_stderr
958 sys.stdin = self.save_stdin
959 # Break cycles
960 self.interp = None
961 self.console = None
962 self.flist.pyshell = None
963 self.history = None
964 EditorWindow._close(self)
966 def ispythonsource(self, filename):
967 "Override EditorWindow method: never remove the colorizer"
968 return True
970 def short_title(self):
971 return self.shell_title
973 COPYRIGHT = \
974 'Type "copyright", "credits" or "license()" for more information.'
976 def begin(self):
977 self.resetoutput()
978 if use_subprocess:
979 nosub = ''
980 client = self.interp.start_subprocess()
981 if not client:
982 self.close()
983 return False
984 else:
985 nosub = "==== No Subprocess ===="
986 self.write("Python %s on %s\n%s\n%s" %
987 (sys.version, sys.platform, self.COPYRIGHT, nosub))
988 self.showprompt()
989 import Tkinter
990 Tkinter._default_root = None # 03Jan04 KBK What's this?
991 return True
993 def readline(self):
994 save = self.reading
995 try:
996 self.reading = 1
997 self.top.mainloop() # nested mainloop()
998 finally:
999 self.reading = save
1000 line = self.text.get("iomark", "end-1c")
1001 if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C
1002 line = "\n"
1003 if isinstance(line, unicode):
1004 import IOBinding
1005 try:
1006 line = line.encode(IOBinding.encoding)
1007 except UnicodeError:
1008 pass
1009 self.resetoutput()
1010 if self.canceled:
1011 self.canceled = 0
1012 if not use_subprocess:
1013 raise KeyboardInterrupt
1014 if self.endoffile:
1015 self.endoffile = 0
1016 line = ""
1017 return line
1019 def isatty(self):
1020 return True
1022 def cancel_callback(self, event=None):
1023 try:
1024 if self.text.compare("sel.first", "!=", "sel.last"):
1025 return # Active selection -- always use default binding
1026 except:
1027 pass
1028 if not (self.executing or self.reading):
1029 self.resetoutput()
1030 self.interp.write("KeyboardInterrupt\n")
1031 self.showprompt()
1032 return "break"
1033 self.endoffile = 0
1034 self.canceled = 1
1035 if (self.executing and self.interp.rpcclt):
1036 if self.interp.getdebugger():
1037 self.interp.restart_subprocess()
1038 else:
1039 self.interp.interrupt_subprocess()
1040 if self.reading:
1041 self.top.quit() # exit the nested mainloop() in readline()
1042 return "break"
1044 def eof_callback(self, event):
1045 if self.executing and not self.reading:
1046 return # Let the default binding (delete next char) take over
1047 if not (self.text.compare("iomark", "==", "insert") and
1048 self.text.compare("insert", "==", "end-1c")):
1049 return # Let the default binding (delete next char) take over
1050 if not self.executing:
1051 self.resetoutput()
1052 self.close()
1053 else:
1054 self.canceled = 0
1055 self.endoffile = 1
1056 self.top.quit()
1057 return "break"
1059 def linefeed_callback(self, event):
1060 # Insert a linefeed without entering anything (still autoindented)
1061 if self.reading:
1062 self.text.insert("insert", "\n")
1063 self.text.see("insert")
1064 else:
1065 self.newline_and_indent_event(event)
1066 return "break"
1068 def enter_callback(self, event):
1069 if self.executing and not self.reading:
1070 return # Let the default binding (insert '\n') take over
1071 # If some text is selected, recall the selection
1072 # (but only if this before the I/O mark)
1073 try:
1074 sel = self.text.get("sel.first", "sel.last")
1075 if sel:
1076 if self.text.compare("sel.last", "<=", "iomark"):
1077 self.recall(sel, event)
1078 return "break"
1079 except:
1080 pass
1081 # If we're strictly before the line containing iomark, recall
1082 # the current line, less a leading prompt, less leading or
1083 # trailing whitespace
1084 if self.text.compare("insert", "<", "iomark linestart"):
1085 # Check if there's a relevant stdin range -- if so, use it
1086 prev = self.text.tag_prevrange("stdin", "insert")
1087 if prev and self.text.compare("insert", "<", prev[1]):
1088 self.recall(self.text.get(prev[0], prev[1]), event)
1089 return "break"
1090 next = self.text.tag_nextrange("stdin", "insert")
1091 if next and self.text.compare("insert lineend", ">=", next[0]):
1092 self.recall(self.text.get(next[0], next[1]), event)
1093 return "break"
1094 # No stdin mark -- just get the current line, less any prompt
1095 indices = self.text.tag_nextrange("console", "insert linestart")
1096 if indices and \
1097 self.text.compare(indices[0], "<=", "insert linestart"):
1098 self.recall(self.text.get(indices[1], "insert lineend"), event)
1099 else:
1100 self.recall(self.text.get("insert linestart", "insert lineend"), event)
1101 return "break"
1102 # If we're between the beginning of the line and the iomark, i.e.
1103 # in the prompt area, move to the end of the prompt
1104 if self.text.compare("insert", "<", "iomark"):
1105 self.text.mark_set("insert", "iomark")
1106 # If we're in the current input and there's only whitespace
1107 # beyond the cursor, erase that whitespace first
1108 s = self.text.get("insert", "end-1c")
1109 if s and not s.strip():
1110 self.text.delete("insert", "end-1c")
1111 # If we're in the current input before its last line,
1112 # insert a newline right at the insert point
1113 if self.text.compare("insert", "<", "end-1c linestart"):
1114 self.newline_and_indent_event(event)
1115 return "break"
1116 # We're in the last line; append a newline and submit it
1117 self.text.mark_set("insert", "end-1c")
1118 if self.reading:
1119 self.text.insert("insert", "\n")
1120 self.text.see("insert")
1121 else:
1122 self.newline_and_indent_event(event)
1123 self.text.tag_add("stdin", "iomark", "end-1c")
1124 self.text.update_idletasks()
1125 if self.reading:
1126 self.top.quit() # Break out of recursive mainloop() in raw_input()
1127 else:
1128 self.runit()
1129 return "break"
1131 def recall(self, s, event):
1132 # remove leading and trailing empty or whitespace lines
1133 s = re.sub(r'^\s*\n', '' , s)
1134 s = re.sub(r'\n\s*$', '', s)
1135 lines = s.split('\n')
1136 self.text.undo_block_start()
1137 try:
1138 self.text.tag_remove("sel", "1.0", "end")
1139 self.text.mark_set("insert", "end-1c")
1140 prefix = self.text.get("insert linestart", "insert")
1141 if prefix.rstrip().endswith(':'):
1142 self.newline_and_indent_event(event)
1143 prefix = self.text.get("insert linestart", "insert")
1144 self.text.insert("insert", lines[0].strip())
1145 if len(lines) > 1:
1146 orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
1147 new_base_indent = re.search(r'^([ \t]*)', prefix).group(0)
1148 for line in lines[1:]:
1149 if line.startswith(orig_base_indent):
1150 # replace orig base indentation with new indentation
1151 line = new_base_indent + line[len(orig_base_indent):]
1152 self.text.insert('insert', '\n'+line.rstrip())
1153 finally:
1154 self.text.see("insert")
1155 self.text.undo_block_stop()
1157 def runit(self):
1158 line = self.text.get("iomark", "end-1c")
1159 # Strip off last newline and surrounding whitespace.
1160 # (To allow you to hit return twice to end a statement.)
1161 i = len(line)
1162 while i > 0 and line[i-1] in " \t":
1163 i = i-1
1164 if i > 0 and line[i-1] == "\n":
1165 i = i-1
1166 while i > 0 and line[i-1] in " \t":
1167 i = i-1
1168 line = line[:i]
1169 more = self.interp.runsource(line)
1171 def open_stack_viewer(self, event=None):
1172 if self.interp.rpcclt:
1173 return self.interp.remote_stack_viewer()
1174 try:
1175 sys.last_traceback
1176 except:
1177 tkMessageBox.showerror("No stack trace",
1178 "There is no stack trace yet.\n"
1179 "(sys.last_traceback is not defined)",
1180 master=self.text)
1181 return
1182 from StackViewer import StackBrowser
1183 sv = StackBrowser(self.root, self.flist)
1185 def view_restart_mark(self, event=None):
1186 self.text.see("iomark")
1187 self.text.see("restart")
1189 def restart_shell(self, event=None):
1190 self.interp.restart_subprocess()
1192 def showprompt(self):
1193 self.resetoutput()
1194 try:
1195 s = str(sys.ps1)
1196 except:
1197 s = ""
1198 self.console.write(s)
1199 self.text.mark_set("insert", "end-1c")
1200 self.set_line_and_column()
1201 self.io.reset_undo()
1203 def resetoutput(self):
1204 source = self.text.get("iomark", "end-1c")
1205 if self.history:
1206 self.history.history_store(source)
1207 if self.text.get("end-2c") != "\n":
1208 self.text.insert("end-1c", "\n")
1209 self.text.mark_set("iomark", "end-1c")
1210 self.set_line_and_column()
1211 sys.stdout.softspace = 0
1213 def write(self, s, tags=()):
1214 try:
1215 self.text.mark_gravity("iomark", "right")
1216 OutputWindow.write(self, s, tags, "iomark")
1217 self.text.mark_gravity("iomark", "left")
1218 except:
1219 pass
1220 if self.canceled:
1221 self.canceled = 0
1222 if not use_subprocess:
1223 raise KeyboardInterrupt
1225 class PseudoFile(object):
1227 def __init__(self, shell, tags, encoding=None):
1228 self.shell = shell
1229 self.tags = tags
1230 self.softspace = 0
1231 self.encoding = encoding
1233 def write(self, s):
1234 self.shell.write(s, self.tags)
1236 def writelines(self, l):
1237 map(self.write, l)
1239 def flush(self):
1240 pass
1242 def isatty(self):
1243 return True
1246 usage_msg = """\
1248 USAGE: idle [-deins] [-t title] [file]*
1249 idle [-dns] [-t title] (-c cmd | -r file) [arg]*
1250 idle [-dns] [-t title] - [arg]*
1252 -h print this help message and exit
1253 -n run IDLE without a subprocess (see Help/IDLE Help for details)
1255 The following options will override the IDLE 'settings' configuration:
1257 -e open an edit window
1258 -i open a shell window
1260 The following options imply -i and will open a shell:
1262 -c cmd run the command in a shell, or
1263 -r file run script from file
1265 -d enable the debugger
1266 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1267 -t title set title of shell window
1269 A default edit window will be bypassed when -c, -r, or - are used.
1271 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1273 Examples:
1275 idle
1276 Open an edit window or shell depending on IDLE's configuration.
1278 idle foo.py foobar.py
1279 Edit the files, also open a shell if configured to start with shell.
1281 idle -est "Baz" foo.py
1282 Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1283 window with the title "Baz".
1285 idle -c "import sys; print sys.argv" "foo"
1286 Open a shell window and run the command, passing "-c" in sys.argv[0]
1287 and "foo" in sys.argv[1].
1289 idle -d -s -r foo.py "Hello World"
1290 Open a shell window, run a startup script, enable the debugger, and
1291 run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1292 sys.argv[1].
1294 echo "import sys; print sys.argv" | idle - "foobar"
1295 Open a shell window, run the script piped in, passing '' in sys.argv[0]
1296 and "foobar" in sys.argv[1].
1299 def main():
1300 global flist, root, use_subprocess
1302 use_subprocess = True
1303 enable_shell = False
1304 enable_edit = False
1305 debug = False
1306 cmd = None
1307 script = None
1308 startup = False
1309 try:
1310 opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
1311 except getopt.error, msg:
1312 sys.stderr.write("Error: %s\n" % str(msg))
1313 sys.stderr.write(usage_msg)
1314 sys.exit(2)
1315 for o, a in opts:
1316 if o == '-c':
1317 cmd = a
1318 enable_shell = True
1319 if o == '-d':
1320 debug = True
1321 enable_shell = True
1322 if o == '-e':
1323 enable_edit = True
1324 if o == '-h':
1325 sys.stdout.write(usage_msg)
1326 sys.exit()
1327 if o == '-i':
1328 enable_shell = True
1329 if o == '-n':
1330 use_subprocess = False
1331 if o == '-r':
1332 script = a
1333 if os.path.isfile(script):
1334 pass
1335 else:
1336 print "No script file: ", script
1337 sys.exit()
1338 enable_shell = True
1339 if o == '-s':
1340 startup = True
1341 enable_shell = True
1342 if o == '-t':
1343 PyShell.shell_title = a
1344 enable_shell = True
1345 if args and args[0] == '-':
1346 cmd = sys.stdin.read()
1347 enable_shell = True
1348 # process sys.argv and sys.path:
1349 for i in range(len(sys.path)):
1350 sys.path[i] = os.path.abspath(sys.path[i])
1351 if args and args[0] == '-':
1352 sys.argv = [''] + args[1:]
1353 elif cmd:
1354 sys.argv = ['-c'] + args
1355 elif script:
1356 sys.argv = [script] + args
1357 elif args:
1358 enable_edit = True
1359 pathx = []
1360 for filename in args:
1361 pathx.append(os.path.dirname(filename))
1362 for dir in pathx:
1363 dir = os.path.abspath(dir)
1364 if not dir in sys.path:
1365 sys.path.insert(0, dir)
1366 else:
1367 dir = os.getcwd()
1368 if not dir in sys.path:
1369 sys.path.insert(0, dir)
1370 # check the IDLE settings configuration (but command line overrides)
1371 edit_start = idleConf.GetOption('main', 'General',
1372 'editor-on-startup', type='bool')
1373 enable_edit = enable_edit or edit_start
1374 enable_shell = enable_shell or not edit_start
1375 # start editor and/or shell windows:
1376 root = Tk(className="Idle")
1378 fixwordbreaks(root)
1379 root.withdraw()
1380 flist = PyShellFileList(root)
1381 macosxSupport.setupApp(root, flist)
1383 if enable_edit:
1384 if not (cmd or script):
1385 for filename in args:
1386 flist.open(filename)
1387 if not args:
1388 flist.new()
1389 if enable_shell:
1390 shell = flist.open_shell()
1391 if not shell:
1392 return # couldn't open shell
1394 if macosxSupport.runningAsOSXApp() and flist.dict:
1395 # On OSX: when the user has double-clicked on a file that causes
1396 # IDLE to be launched the shell window will open just in front of
1397 # the file she wants to see. Lower the interpreter window when
1398 # there are open files.
1399 shell.top.lower()
1401 shell = flist.pyshell
1402 # handle remaining options:
1403 if debug:
1404 shell.open_debugger()
1405 if startup:
1406 filename = os.environ.get("IDLESTARTUP") or \
1407 os.environ.get("PYTHONSTARTUP")
1408 if filename and os.path.isfile(filename):
1409 shell.interp.execfile(filename)
1410 if shell and cmd or script:
1411 shell.interp.runcommand("""if 1:
1412 import sys as _sys
1413 _sys.argv = %r
1414 del _sys
1415 \n""" % (sys.argv,))
1416 if cmd:
1417 shell.interp.execsource(cmd)
1418 elif script:
1419 shell.interp.prepend_syspath(script)
1420 shell.interp.execfile(script)
1422 root.mainloop()
1423 root.destroy()
1425 if __name__ == "__main__":
1426 sys.modules['PyShell'] = sys.modules['__main__']
1427 main()