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