17 from code
import InteractiveInterpreter
22 print>>sys
.__stderr
__, "** IDLE can't import Tkinter. " \
23 "Your Python may not be configured for Tk. **"
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
39 IDENTCHARS
= string
.ascii_letters
+ string
.digits
+ "_"
40 LOCALHOST
= '127.0.0.1'
43 from signal
import SIGTERM
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.
52 warning_stream
= sys
.__stderr
__
58 def idle_showwarning(message
, category
, filename
, lineno
,
59 file=None, line
=None):
62 file.write(warnings
.formatwarning(message
, category
, filename
,\
63 lineno
, file=file, line
=line
))
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
76 s
+= "%s: %s\n>>> " % (category
.__name
__, message
)
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.
92 cache
= linecache
.cache
94 for filename
in cache
.keys():
95 if filename
[:1] + filename
[-1:] == '<>':
96 save
[filename
] = cache
[filename
]
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(),
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
,
120 self
.restore_file_breaks()
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
):
129 filename
= self
.io
.filename
130 text
.tag_add("BREAK", "%d.0" % lineno
, "%d.0" % (lineno
+1))
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....
141 def set_breakpoint_here(self
, event
=None):
143 filename
= self
.io
.filename
147 lineno
= int(float(text
.index("insert")))
148 self
.set_breakpoint(lineno
)
150 def clear_breakpoint_here(self
, event
=None):
152 filename
= self
.io
.filename
156 lineno
= int(float(text
.index("insert")))
158 self
.breakpoints
.remove(lineno
)
161 text
.tag_remove("BREAK", "insert linestart",\
162 "insert lineend +1char")
164 debug
= self
.flist
.pyshell
.interp
.debugger
165 debug
.clear_breakpoint_here(filename
, lineno
)
169 def clear_file_breaks(self
):
172 filename
= self
.io
.filename
176 self
.breakpoints
= []
177 text
.tag_remove("BREAK", "1.0", END
)
179 debug
= self
.flist
.pyshell
.interp
.debugger
180 debug
.clear_file_breaks(filename
)
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
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
210 lines
= open(self
.breakpointPath
,"r").readlines()
213 new_file
= open(self
.breakpointPath
,"w")
215 if not line
.startswith(filename
+ '='):
217 self
.update_breakpoints()
218 breaks
= self
.breakpoints
220 new_file
.write(filename
+ '=' + str(breaks
) + '\n')
223 def restore_file_breaks(self
):
224 self
.text
.update() # this enables setting "BREAK" tags to be visible
225 filename
= self
.io
.filename
228 if os
.path
.isfile(self
.breakpointPath
):
229 lines
= open(self
.breakpointPath
,"r").readlines()
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"
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
):
245 for index
in range(0, len(ranges
), 2):
246 lineno
= int(float(ranges
[index
]))
247 end
= int(float(ranges
[index
+1]))
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)
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
275 def open_shell(self
, event
=None):
277 self
.pyshell
.top
.wakeup()
279 self
.pyshell
= PyShell(self
)
281 if not self
.pyshell
.begin():
286 class ModifiedColorDelegator(ColorDelegator
):
287 "Extend base class: colorizer for the shell window itself"
290 ColorDelegator
.__init
__(self
)
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):
313 if self
.delegate
.compare(index
, "<", "iomark"):
318 UndoDelegator
.insert(self
, index
, chars
, tags
)
320 def delete(self
, index1
, index2
=None):
322 if self
.delegate
.compare(index1
, "<", "iomark"):
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"
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()
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
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
,)
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
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
384 self
.rpcclt
= MyRPCClient(addr
)
386 except socket
.error
, err
:
389 self
.display_port_binding_error()
391 # Accept the connection from the Python execution server
392 self
.rpcclt
.listening_sock
.settimeout(10)
395 except socket
.timeout
, err
:
396 self
.display_no_subprocess_error()
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
)
405 self
.poll_subprocess()
408 def restart_subprocess(self
):
411 self
.restarting
= True
412 # close only the subprocess debugger
413 debug
= self
.getdebugger()
416 # Only close subprocess debugger, don't unregister gui_adap!
417 RemoteDebugger
.close_subprocess_debugger(self
.rpcclt
)
420 # Kill subprocess, spawn a new one, accept connection.
422 self
.unix_terminate()
423 console
= self
.tkconsole
424 was_executing
= console
.executing
425 console
.executing
= False
426 self
.spawn_subprocess()
429 except socket
.timeout
, err
:
430 self
.display_no_subprocess_error()
433 # annotate restart in shell window and mark it
434 console
.text
.delete("iomark", "end-1c")
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")
443 # restart subprocess debugger
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
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
):
461 except AttributeError: # no socket
463 self
.unix_terminate()
464 self
.tkconsole
.executing
= False
467 def unix_terminate(self
):
468 "UNIX: make sure subprocess is terminated and collect status"
469 if hasattr(os
, 'kill'):
471 os
.kill(self
.rpcpid
, SIGTERM
)
473 # process already terminated:
477 os
.waitpid(self
.rpcpid
, 0)
481 def transfer_path(self
):
482 self
.runcommand("""if 1:
490 def poll_subprocess(self
):
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
:
502 self
.restart_subprocess()
504 self
.tkconsole
.resetoutput()
505 self
.active_seq
= None
507 console
= self
.tkconsole
.console
510 print >>console
, repr(what
)
511 elif how
== "EXCEPTION":
512 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
513 self
.remote_stack_viewer()
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:
520 self
.tkconsole
.endexecuting()
521 except AttributeError: # shell may have closed
524 if not self
.tkconsole
.closing
:
525 self
.tkconsole
.text
.after(self
.tkconsole
.pollinterval
,
526 self
.poll_subprocess
)
530 def setdebugger(self
, debugger
):
531 self
.debugger
= debugger
533 def getdebugger(self
):
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
546 self
.tkconsole
.text
.after(300, self
.remote_stack_viewer
)
549 def remote_stack_viewer(self
):
550 import RemoteObjectBrowser
551 oid
= self
.rpcclt
.remotequeue("exec", "stackviewer", ("flist",), {})
553 self
.tkconsole
.root
.bell()
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
)
564 # XXX Should GC the remote tree when closing the window
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"
576 source
= open(filename
, "r").read()
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()
589 def runsource(self
, source
):
590 "Extend base class method: Stuff the source in the line cache first"
591 filename
= self
.stuffsource(source
)
593 self
.save_warnings_filters
= warnings
.filters
[:]
594 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
595 if isinstance(source
, types
.UnicodeType
):
598 source
= source
.encode(IOBinding
.encoding
)
600 self
.tkconsole
.resetoutput()
601 self
.write("Unsupported characters in input\n")
604 # InteractiveInterpreter.runsource() calls its runcode() method,
605 # which is overridden (see below)
606 return InteractiveInterpreter
.runsource(self
, source
, filename
)
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
620 def prepend_syspath(self
, filename
):
621 "Prepend sys.path with file's directory if not already included"
622 self
.runcommand("""if 1:
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
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
639 text
= self
.tkconsole
.text
640 stuff
= self
.unpackerror()
642 msg
, lineno
, offset
, line
= stuff
644 pos
= "iomark + %d chars" % (offset
-1)
646 pos
= "iomark linestart + %d lines + %d chars" % \
648 text
.tag_add("ERROR", 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
))
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
665 msg
, (dummy_filename
, lineno
, offset
, line
) = value
671 return msg
, lineno
, offset
, line
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
):
686 if key
[:1] + key
[-1:] != "<>":
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()
696 self
.rpcclt
.remotequeue("exec", "runcode", (code
,), {})
698 exec code
in self
.locals
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
711 self
.tkconsole
.beginexecuting()
712 if not debugger
and self
.rpcclt
is not None:
713 self
.active_seq
= self
.rpcclt
.asyncqueue("exec", "runcode",
716 debugger
.run(code
, self
.locals)
718 exec code
in self
.locals
720 if not self
.tkconsole
.closing
:
721 if tkMessageBox
.askyesno(
723 "Do you want to exit altogether?",
725 master
=self
.tkconsole
.text
):
733 print >>self
.tkconsole
.stderr
, \
734 "IDLE internal error in runcode()"
736 self
.tkconsole
.endexecuting()
738 if self
.tkconsole
.canceled
:
739 self
.tkconsole
.canceled
= False
740 print >>self
.tkconsole
.stderr
, "KeyboardInterrupt"
744 if not use_subprocess
:
746 self
.tkconsole
.endexecuting()
747 except AttributeError: # shell may have closed
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 "
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 "
772 master
=self
.tkconsole
.text
)
774 def display_executing_dialog(self
):
775 tkMessageBox
.showerror(
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"
787 ColorDelegator
= ModifiedColorDelegator
788 UndoDelegator
= ModifiedUndoDelegator
795 ("options", "_Options"),
796 ("windows", "_Windows"),
800 if macosxSupport
.runningAsOSXApp():
802 menu_specs
[-2] = ("windows", "_Window")
806 from IdleHistory
import History
808 def __init__(self
, flist
=None):
811 if ms
[2][0] != "shell":
812 ms
.insert(2, ("shell", "She_ll"))
813 self
.interp
= ModifiedInterpreter(self
)
818 flist
= PyShellFileList(root
)
820 OutputWindow
.__init
__(self
, flist
, None, None)
822 ## self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
824 # indentwidth must be 8 when using tabs. See note in EditorWindow:
826 self
.context_use_ps1
= True
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
)
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
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
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)
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):
875 tkMessageBox
.showerror("Don't debug now",
876 "You can only toggle the debugger when idle",
878 self
.set_debugger_indicator()
881 db
= self
.interp
.getdebugger()
883 self
.close_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()
897 self
.interp
.setdebugger(None)
899 if self
.interp
.rpcclt
:
900 RemoteDebugger
.close_remote_debugger(self
.interp
.rpcclt
)
902 self
.console
.write("[DEBUG OFF]\n")
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
,
912 dbg_gui
= Debugger
.Debugger(self
)
913 self
.interp
.setdebugger(dbg_gui
)
914 dbg_gui
.load_breakpoints()
915 sys
.ps1
= "[DEBUG ON]\n>>> "
917 self
.set_debugger_indicator()
919 def beginexecuting(self
):
920 "Helper for ModifiedInterpreter"
924 def endexecuting(self
):
925 "Helper for ModifiedInterpreter"
931 "Extend EditorWindow.close()"
933 response
= tkMessageBox
.askokcancel(
935 "The program is still running!\n Do you want to kill it?",
938 if response
is False:
944 # Wait for poll_subprocess() rescheduling to stop
945 self
.text
.after(2 * self
.pollinterval
, self
.close2
)
948 return EditorWindow
.close(self
)
951 "Extend EditorWindow._close(), shut down debugger and execution server"
952 self
.close_debugger()
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
962 self
.flist
.pyshell
= None
964 EditorWindow
._close
(self
)
966 def ispythonsource(self
, filename
):
967 "Override EditorWindow method: never remove the colorizer"
970 def short_title(self
):
971 return self
.shell_title
974 'Type "copyright", "credits" or "license()" for more information.'
980 client
= self
.interp
.start_subprocess()
985 nosub
= "==== No Subprocess ===="
986 self
.write("Python %s on %s\n%s\n%s" %
987 (sys
.version
, sys
.platform
, self
.COPYRIGHT
, nosub
))
990 Tkinter
._default
_root
= None # 03Jan04 KBK What's this?
997 self
.top
.mainloop() # nested mainloop()
1000 line
= self
.text
.get("iomark", "end-1c")
1001 if len(line
) == 0: # may be EOF if we quit our mainloop with Ctrl-C
1003 if isinstance(line
, unicode):
1006 line
= line
.encode(IOBinding
.encoding
)
1007 except UnicodeError:
1012 if not use_subprocess
:
1013 raise KeyboardInterrupt
1022 def cancel_callback(self
, event
=None):
1024 if self
.text
.compare("sel.first", "!=", "sel.last"):
1025 return # Active selection -- always use default binding
1028 if not (self
.executing
or self
.reading
):
1030 self
.interp
.write("KeyboardInterrupt\n")
1035 if (self
.executing
and self
.interp
.rpcclt
):
1036 if self
.interp
.getdebugger():
1037 self
.interp
.restart_subprocess()
1039 self
.interp
.interrupt_subprocess()
1041 self
.top
.quit() # exit the nested mainloop() in readline()
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
:
1059 def linefeed_callback(self
, event
):
1060 # Insert a linefeed without entering anything (still autoindented)
1062 self
.text
.insert("insert", "\n")
1063 self
.text
.see("insert")
1065 self
.newline_and_indent_event(event
)
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)
1074 sel
= self
.text
.get("sel.first", "sel.last")
1076 if self
.text
.compare("sel.last", "<=", "iomark"):
1077 self
.recall(sel
, event
)
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
)
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
)
1094 # No stdin mark -- just get the current line, less any prompt
1095 indices
= self
.text
.tag_nextrange("console", "insert linestart")
1097 self
.text
.compare(indices
[0], "<=", "insert linestart"):
1098 self
.recall(self
.text
.get(indices
[1], "insert lineend"), event
)
1100 self
.recall(self
.text
.get("insert linestart", "insert lineend"), event
)
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
)
1116 # We're in the last line; append a newline and submit it
1117 self
.text
.mark_set("insert", "end-1c")
1119 self
.text
.insert("insert", "\n")
1120 self
.text
.see("insert")
1122 self
.newline_and_indent_event(event
)
1123 self
.text
.tag_add("stdin", "iomark", "end-1c")
1124 self
.text
.update_idletasks()
1126 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
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()
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())
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())
1154 self
.text
.see("insert")
1155 self
.text
.undo_block_stop()
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.)
1162 while i
> 0 and line
[i
-1] in " \t":
1164 if i
> 0 and line
[i
-1] == "\n":
1166 while i
> 0 and line
[i
-1] in " \t":
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()
1177 tkMessageBox
.showerror("No stack trace",
1178 "There is no stack trace yet.\n"
1179 "(sys.last_traceback is not defined)",
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
):
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")
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
=()):
1215 self
.text
.mark_gravity("iomark", "right")
1216 OutputWindow
.write(self
, s
, tags
, "iomark")
1217 self
.text
.mark_gravity("iomark", "left")
1222 if not use_subprocess
:
1223 raise KeyboardInterrupt
1225 class PseudoFile(object):
1227 def __init__(self
, shell
, tags
, encoding
=None):
1231 self
.encoding
= encoding
1234 self
.shell
.write(s
, self
.tags
)
1236 def writelines(self
, l
):
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:].
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
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].
1300 global flist
, root
, use_subprocess
1302 use_subprocess
= True
1303 enable_shell
= False
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
)
1325 sys
.stdout
.write(usage_msg
)
1330 use_subprocess
= False
1333 if os
.path
.isfile(script
):
1336 print "No script file: ", script
1343 PyShell
.shell_title
= a
1345 if args
and args
[0] == '-':
1346 cmd
= sys
.stdin
.read()
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:]
1354 sys
.argv
= ['-c'] + args
1356 sys
.argv
= [script
] + args
1360 for filename
in args
:
1361 pathx
.append(os
.path
.dirname(filename
))
1363 dir = os
.path
.abspath(dir)
1364 if not dir in sys
.path
:
1365 sys
.path
.insert(0, dir)
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")
1380 flist
= PyShellFileList(root
)
1381 macosxSupport
.setupApp(root
, flist
)
1384 if not (cmd
or script
):
1385 for filename
in args
:
1386 flist
.open(filename
)
1390 shell
= flist
.open_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.
1401 shell
= flist
.pyshell
1402 # handle remaining options:
1404 shell
.open_debugger()
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:
1415 \n""" % (sys
.argv
,))
1417 shell
.interp
.execsource(cmd
)
1419 shell
.interp
.prepend_syspath(script
)
1420 shell
.interp
.execfile(script
)
1425 if __name__
== "__main__":
1426 sys
.modules
['PyShell'] = sys
.modules
['__main__']