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
):
61 file.write(warnings
.formatwarning(message
, category
, filename
, lineno
))
63 pass ## file (probably __stderr__) is invalid, warning dropped.
64 warnings
.showwarning
= idle_showwarning
65 def idle_formatwarning(message
, category
, filename
, lineno
):
66 """Format warnings the IDLE way"""
67 s
= "\nWarning (from warnings module):\n"
68 s
+= ' File \"%s\", line %s\n' % (filename
, lineno
)
69 line
= linecache
.getline(filename
, lineno
).strip()
72 s
+= "%s: %s\n>>> " % (category
.__name
__, message
)
74 warnings
.formatwarning
= idle_formatwarning
76 def extended_linecache_checkcache(filename
=None,
77 orig_checkcache
=linecache
.checkcache
):
78 """Extend linecache.checkcache to preserve the <pyshell#...> entries
80 Rather than repeating the linecache code, patch it to save the
81 <pyshell#...> entries, call the original linecache.checkcache()
82 (which destroys them), and then restore the saved entries.
84 orig_checkcache is bound at definition time to the original
85 method, allowing it to be patched.
88 cache
= linecache
.cache
90 for filename
in cache
.keys():
91 if filename
[:1] + filename
[-1:] == '<>':
92 save
[filename
] = cache
[filename
]
96 # Patch linecache.checkcache():
97 linecache
.checkcache
= extended_linecache_checkcache
100 class PyShellEditorWindow(EditorWindow
):
101 "Regular text edit window in IDLE, supports breakpoints"
103 def __init__(self
, *args
):
104 self
.breakpoints
= []
105 EditorWindow
.__init
__(self
, *args
)
106 self
.text
.bind("<<set-breakpoint-here>>", self
.set_breakpoint_here
)
107 self
.text
.bind("<<clear-breakpoint-here>>", self
.clear_breakpoint_here
)
108 self
.text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
110 self
.breakpointPath
= os
.path
.join(idleConf
.GetUserCfgDir(),
112 # whenever a file is changed, restore breakpoints
113 if self
.io
.filename
: self
.restore_file_breaks()
114 def filename_changed_hook(old_hook
=self
.io
.filename_change_hook
,
116 self
.restore_file_breaks()
118 self
.io
.set_filename_change_hook(filename_changed_hook
)
120 rmenu_specs
= [("Set Breakpoint", "<<set-breakpoint-here>>"),
121 ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
123 def set_breakpoint(self
, lineno
):
125 filename
= self
.io
.filename
126 text
.tag_add("BREAK", "%d.0" % lineno
, "%d.0" % (lineno
+1))
128 i
= self
.breakpoints
.index(lineno
)
129 except ValueError: # only add if missing, i.e. do once
130 self
.breakpoints
.append(lineno
)
131 try: # update the subprocess debugger
132 debug
= self
.flist
.pyshell
.interp
.debugger
133 debug
.set_breakpoint_here(filename
, lineno
)
134 except: # but debugger may not be active right now....
137 def set_breakpoint_here(self
, event
=None):
139 filename
= self
.io
.filename
143 lineno
= int(float(text
.index("insert")))
144 self
.set_breakpoint(lineno
)
146 def clear_breakpoint_here(self
, event
=None):
148 filename
= self
.io
.filename
152 lineno
= int(float(text
.index("insert")))
154 self
.breakpoints
.remove(lineno
)
157 text
.tag_remove("BREAK", "insert linestart",\
158 "insert lineend +1char")
160 debug
= self
.flist
.pyshell
.interp
.debugger
161 debug
.clear_breakpoint_here(filename
, lineno
)
165 def clear_file_breaks(self
):
168 filename
= self
.io
.filename
172 self
.breakpoints
= []
173 text
.tag_remove("BREAK", "1.0", END
)
175 debug
= self
.flist
.pyshell
.interp
.debugger
176 debug
.clear_file_breaks(filename
)
180 def store_file_breaks(self
):
181 "Save breakpoints when file is saved"
182 # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
183 # be run. The breaks are saved at that time. If we introduce
184 # a temporary file save feature the save breaks functionality
185 # needs to be re-verified, since the breaks at the time the
186 # temp file is created may differ from the breaks at the last
187 # permanent save of the file. Currently, a break introduced
188 # after a save will be effective, but not persistent.
189 # This is necessary to keep the saved breaks synched with the
192 # Breakpoints are set as tagged ranges in the text. Certain
193 # kinds of edits cause these ranges to be deleted: Inserting
194 # or deleting a line just before a breakpoint, and certain
195 # deletions prior to a breakpoint. These issues need to be
196 # investigated and understood. It's not clear if they are
197 # Tk issues or IDLE issues, or whether they can actually
198 # be fixed. Since a modified file has to be saved before it is
199 # run, and since self.breakpoints (from which the subprocess
200 # debugger is loaded) is updated during the save, the visible
201 # breaks stay synched with the subprocess even if one of these
202 # unexpected breakpoint deletions occurs.
203 breaks
= self
.breakpoints
204 filename
= self
.io
.filename
206 lines
= open(self
.breakpointPath
,"r").readlines()
209 new_file
= open(self
.breakpointPath
,"w")
211 if not line
.startswith(filename
+ '='):
213 self
.update_breakpoints()
214 breaks
= self
.breakpoints
216 new_file
.write(filename
+ '=' + str(breaks
) + '\n')
219 def restore_file_breaks(self
):
220 self
.text
.update() # this enables setting "BREAK" tags to be visible
221 filename
= self
.io
.filename
224 if os
.path
.isfile(self
.breakpointPath
):
225 lines
= open(self
.breakpointPath
,"r").readlines()
227 if line
.startswith(filename
+ '='):
228 breakpoint_linenumbers
= eval(line
[len(filename
)+1:])
229 for breakpoint_linenumber
in breakpoint_linenumbers
:
230 self
.set_breakpoint(breakpoint_linenumber
)
232 def update_breakpoints(self
):
233 "Retrieves all the breakpoints in the current window"
235 ranges
= text
.tag_ranges("BREAK")
236 linenumber_list
= self
.ranges_to_linenumbers(ranges
)
237 self
.breakpoints
= linenumber_list
239 def ranges_to_linenumbers(self
, ranges
):
241 for index
in range(0, len(ranges
), 2):
242 lineno
= int(float(ranges
[index
]))
243 end
= int(float(ranges
[index
+1]))
249 # XXX 13 Dec 2002 KBK Not used currently
250 # def saved_change_hook(self):
251 # "Extend base method - clear breaks if module is modified"
252 # if not self.get_saved():
253 # self.clear_file_breaks()
254 # EditorWindow.saved_change_hook(self)
257 "Extend base method - clear breaks when module is closed"
258 self
.clear_file_breaks()
259 EditorWindow
._close
(self
)
262 class PyShellFileList(FileList
):
263 "Extend base class: IDLE supports a shell and breakpoints"
265 # override FileList's class variable, instances return PyShellEditorWindow
266 # instead of EditorWindow when new edit windows are created.
267 EditorWindow
= PyShellEditorWindow
271 def open_shell(self
, event
=None):
273 self
.pyshell
.top
.wakeup()
275 self
.pyshell
= PyShell(self
)
277 if not self
.pyshell
.begin():
282 class ModifiedColorDelegator(ColorDelegator
):
283 "Extend base class: colorizer for the shell window itself"
286 ColorDelegator
.__init
__(self
)
289 def recolorize_main(self
):
290 self
.tag_remove("TODO", "1.0", "iomark")
291 self
.tag_add("SYNC", "1.0", "iomark")
292 ColorDelegator
.recolorize_main(self
)
294 def LoadTagDefs(self
):
295 ColorDelegator
.LoadTagDefs(self
)
296 theme
= idleConf
.GetOption('main','Theme','name')
297 self
.tagdefs
.update({
298 "stdin": {'background':None,'foreground':None},
299 "stdout": idleConf
.GetHighlight(theme
, "stdout"),
300 "stderr": idleConf
.GetHighlight(theme
, "stderr"),
301 "console": idleConf
.GetHighlight(theme
, "console"),
304 class ModifiedUndoDelegator(UndoDelegator
):
305 "Extend base class: forbid insert/delete before the I/O mark"
307 def insert(self
, index
, chars
, tags
=None):
309 if self
.delegate
.compare(index
, "<", "iomark"):
314 UndoDelegator
.insert(self
, index
, chars
, tags
)
316 def delete(self
, index1
, index2
=None):
318 if self
.delegate
.compare(index1
, "<", "iomark"):
323 UndoDelegator
.delete(self
, index1
, index2
)
326 class MyRPCClient(rpc
.RPCClient
):
328 def handle_EOF(self
):
329 "Override the base class - just re-raise EOFError"
333 class ModifiedInterpreter(InteractiveInterpreter
):
335 def __init__(self
, tkconsole
):
336 self
.tkconsole
= tkconsole
337 locals = sys
.modules
['__main__'].__dict
__
338 InteractiveInterpreter
.__init
__(self
, locals=locals)
339 self
.save_warnings_filters
= None
340 self
.restarting
= False
341 self
.subprocess_arglist
= self
.build_subprocess_arglist()
347 def spawn_subprocess(self
):
348 args
= self
.subprocess_arglist
349 self
.rpcpid
= os
.spawnv(os
.P_NOWAIT
, sys
.executable
, args
)
351 def build_subprocess_arglist(self
):
352 w
= ['-W' + s
for s
in sys
.warnoptions
]
353 if 1/2 > 0: # account for new division
355 # Maybe IDLE is installed and is being accessed via sys.path,
356 # or maybe it's not installed and the idle.py script is being
357 # run from the IDLE source directory.
358 del_exitf
= idleConf
.GetOption('main', 'General', 'delete-exitfunc',
359 default
=False, type='bool')
360 if __name__
== 'idlelib.PyShell':
361 command
= "__import__('idlelib.run').run.main(%r)" % (del_exitf
,)
363 command
= "__import__('run').main(%r)" % (del_exitf
,)
364 if sys
.platform
[:3] == 'win' and ' ' in sys
.executable
:
365 # handle embedded space in path by quoting the argument
366 decorated_exec
= '"%s"' % sys
.executable
368 decorated_exec
= sys
.executable
369 return [decorated_exec
] + w
+ ["-c", command
, str(self
.port
)]
371 def start_subprocess(self
):
372 # spawning first avoids passing a listening socket to the subprocess
373 self
.spawn_subprocess()
374 #time.sleep(20) # test to simulate GUI not accepting connection
375 addr
= (LOCALHOST
, self
.port
)
376 # Idle starts listening for connection on localhost
380 self
.rpcclt
= MyRPCClient(addr
)
382 except socket
.error
, err
:
385 self
.display_port_binding_error()
387 # Accept the connection from the Python execution server
388 self
.rpcclt
.listening_sock
.settimeout(10)
391 except socket
.timeout
, err
:
392 self
.display_no_subprocess_error()
394 self
.rpcclt
.register("stdin", self
.tkconsole
)
395 self
.rpcclt
.register("stdout", self
.tkconsole
.stdout
)
396 self
.rpcclt
.register("stderr", self
.tkconsole
.stderr
)
397 self
.rpcclt
.register("flist", self
.tkconsole
.flist
)
398 self
.rpcclt
.register("linecache", linecache
)
399 self
.rpcclt
.register("interp", self
)
401 self
.poll_subprocess()
404 def restart_subprocess(self
):
407 self
.restarting
= True
408 # close only the subprocess debugger
409 debug
= self
.getdebugger()
412 # Only close subprocess debugger, don't unregister gui_adap!
413 RemoteDebugger
.close_subprocess_debugger(self
.rpcclt
)
416 # Kill subprocess, spawn a new one, accept connection.
418 self
.unix_terminate()
419 console
= self
.tkconsole
420 was_executing
= console
.executing
421 console
.executing
= False
422 self
.spawn_subprocess()
425 except socket
.timeout
, err
:
426 self
.display_no_subprocess_error()
429 # annotate restart in shell window and mark it
430 console
.text
.delete("iomark", "end-1c")
434 halfbar
= ((int(console
.width
) - 16) // 2) * '='
435 console
.write(halfbar
+ ' RESTART ' + halfbar
)
436 console
.text
.mark_set("restart", "end-1c")
437 console
.text
.mark_gravity("restart", "left")
439 # restart subprocess debugger
441 # Restarted debugger connects to current instance of debug GUI
442 gui
= RemoteDebugger
.restart_subprocess_debugger(self
.rpcclt
)
443 # reload remote debugger breakpoints for all PyShellEditWindows
444 debug
.load_breakpoints()
445 self
.restarting
= False
448 def __request_interrupt(self
):
449 self
.rpcclt
.remotecall("exec", "interrupt_the_server", (), {})
451 def interrupt_subprocess(self
):
452 threading
.Thread(target
=self
.__request
_interrupt
).start()
454 def kill_subprocess(self
):
457 except AttributeError: # no socket
459 self
.unix_terminate()
460 self
.tkconsole
.executing
= False
463 def unix_terminate(self
):
464 "UNIX: make sure subprocess is terminated and collect status"
465 if hasattr(os
, 'kill'):
467 os
.kill(self
.rpcpid
, SIGTERM
)
469 # process already terminated:
473 os
.waitpid(self
.rpcpid
, 0)
477 def transfer_path(self
):
478 self
.runcommand("""if 1:
486 def poll_subprocess(self
):
491 response
= clt
.pollresponse(self
.active_seq
, wait
=0.05)
492 except (EOFError, IOError, KeyboardInterrupt):
493 # lost connection or subprocess terminated itself, restart
494 # [the KBI is from rpc.SocketIO.handle_EOF()]
495 if self
.tkconsole
.closing
:
498 self
.restart_subprocess()
500 self
.tkconsole
.resetoutput()
501 self
.active_seq
= None
503 console
= self
.tkconsole
.console
506 print >>console
, repr(what
)
507 elif how
== "EXCEPTION":
508 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
509 self
.remote_stack_viewer()
511 errmsg
= "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
512 print >>sys
.__stderr
__, errmsg
, what
513 print >>console
, errmsg
, what
514 # we received a response to the currently active seq number:
516 self
.tkconsole
.endexecuting()
517 except AttributeError: # shell may have closed
520 if not self
.tkconsole
.closing
:
521 self
.tkconsole
.text
.after(self
.tkconsole
.pollinterval
,
522 self
.poll_subprocess
)
526 def setdebugger(self
, debugger
):
527 self
.debugger
= debugger
529 def getdebugger(self
):
532 def open_remote_stack_viewer(self
):
533 """Initiate the remote stack viewer from a separate thread.
535 This method is called from the subprocess, and by returning from this
536 method we allow the subprocess to unblock. After a bit the shell
537 requests the subprocess to open the remote stack viewer which returns a
538 static object looking at the last exceptiopn. It is queried through
542 self
.tkconsole
.text
.after(300, self
.remote_stack_viewer
)
545 def remote_stack_viewer(self
):
546 import RemoteObjectBrowser
547 oid
= self
.rpcclt
.remotequeue("exec", "stackviewer", ("flist",), {})
549 self
.tkconsole
.root
.bell()
551 item
= RemoteObjectBrowser
.StubObjectTreeItem(self
.rpcclt
, oid
)
552 from TreeWidget
import ScrolledCanvas
, TreeNode
553 top
= Toplevel(self
.tkconsole
.root
)
554 theme
= idleConf
.GetOption('main','Theme','name')
555 background
= idleConf
.GetHighlight(theme
, 'normal')['background']
556 sc
= ScrolledCanvas(top
, bg
=background
, highlightthickness
=0)
557 sc
.frame
.pack(expand
=1, fill
="both")
558 node
= TreeNode(sc
.canvas
, None, item
)
560 # XXX Should GC the remote tree when closing the window
564 def execsource(self
, source
):
565 "Like runsource() but assumes complete exec source"
566 filename
= self
.stuffsource(source
)
567 self
.execfile(filename
, source
)
569 def execfile(self
, filename
, source
=None):
570 "Execute an existing file"
572 source
= open(filename
, "r").read()
574 code
= compile(source
, filename
, "exec")
575 except (OverflowError, SyntaxError):
576 self
.tkconsole
.resetoutput()
577 tkerr
= self
.tkconsole
.stderr
578 print>>tkerr
, '*** Error in script or command!\n'
579 print>>tkerr
, 'Traceback (most recent call last):'
580 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
581 self
.tkconsole
.showprompt()
585 def runsource(self
, source
):
586 "Extend base class method: Stuff the source in the line cache first"
587 filename
= self
.stuffsource(source
)
589 self
.save_warnings_filters
= warnings
.filters
[:]
590 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
591 if isinstance(source
, types
.UnicodeType
):
594 source
= source
.encode(IOBinding
.encoding
)
596 self
.tkconsole
.resetoutput()
597 self
.write("Unsupported characters in input\n")
600 # InteractiveInterpreter.runsource() calls its runcode() method,
601 # which is overridden (see below)
602 return InteractiveInterpreter
.runsource(self
, source
, filename
)
604 if self
.save_warnings_filters
is not None:
605 warnings
.filters
[:] = self
.save_warnings_filters
606 self
.save_warnings_filters
= None
608 def stuffsource(self
, source
):
609 "Stuff source in the filename cache"
610 filename
= "<pyshell#%d>" % self
.gid
611 self
.gid
= self
.gid
+ 1
612 lines
= source
.split("\n")
613 linecache
.cache
[filename
] = len(source
)+1, 0, lines
, filename
616 def prepend_syspath(self
, filename
):
617 "Prepend sys.path with file's directory if not already included"
618 self
.runcommand("""if 1:
621 from os.path import dirname as _dirname
622 _dir = _dirname(_filename)
623 if not _dir in _sys.path:
624 _sys.path.insert(0, _dir)
625 del _filename, _sys, _dirname, _dir
628 def showsyntaxerror(self
, filename
=None):
629 """Extend base class method: Add Colorizing
631 Color the offending position instead of printing it and pointing at it
635 text
= self
.tkconsole
.text
636 stuff
= self
.unpackerror()
638 msg
, lineno
, offset
, line
= stuff
640 pos
= "iomark + %d chars" % (offset
-1)
642 pos
= "iomark linestart + %d lines + %d chars" % \
644 text
.tag_add("ERROR", pos
)
647 if char
and char
in IDENTCHARS
:
648 text
.tag_add("ERROR", pos
+ " wordstart", pos
)
649 self
.tkconsole
.resetoutput()
650 self
.write("SyntaxError: %s\n" % str(msg
))
652 self
.tkconsole
.resetoutput()
653 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
654 self
.tkconsole
.showprompt()
656 def unpackerror(self
):
657 type, value
, tb
= sys
.exc_info()
658 ok
= type is SyntaxError
661 msg
, (dummy_filename
, lineno
, offset
, line
) = value
667 return msg
, lineno
, offset
, line
671 def showtraceback(self
):
672 "Extend base class method to reset output properly"
673 self
.tkconsole
.resetoutput()
674 self
.checklinecache()
675 InteractiveInterpreter
.showtraceback(self
)
676 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
677 self
.tkconsole
.open_stack_viewer()
679 def checklinecache(self
):
682 if key
[:1] + key
[-1:] != "<>":
685 def runcommand(self
, code
):
686 "Run the code without invoking the debugger"
687 # The code better not raise an exception!
688 if self
.tkconsole
.executing
:
689 self
.display_executing_dialog()
692 self
.rpcclt
.remotequeue("exec", "runcode", (code
,), {})
694 exec code
in self
.locals
697 def runcode(self
, code
):
698 "Override base class method"
699 if self
.tkconsole
.executing
:
700 self
.interp
.restart_subprocess()
701 self
.checklinecache()
702 if self
.save_warnings_filters
is not None:
703 warnings
.filters
[:] = self
.save_warnings_filters
704 self
.save_warnings_filters
= None
705 debugger
= self
.debugger
707 self
.tkconsole
.beginexecuting()
708 if not debugger
and self
.rpcclt
is not None:
709 self
.active_seq
= self
.rpcclt
.asyncqueue("exec", "runcode",
712 debugger
.run(code
, self
.locals)
714 exec code
in self
.locals
716 if not self
.tkconsole
.closing
:
717 if tkMessageBox
.askyesno(
719 "Do you want to exit altogether?",
721 master
=self
.tkconsole
.text
):
729 print >>self
.tkconsole
.stderr
, \
730 "IDLE internal error in runcode()"
732 self
.tkconsole
.endexecuting()
734 if self
.tkconsole
.canceled
:
735 self
.tkconsole
.canceled
= False
736 print >>self
.tkconsole
.stderr
, "KeyboardInterrupt"
740 if not use_subprocess
:
742 self
.tkconsole
.endexecuting()
743 except AttributeError: # shell may have closed
747 "Override base class method"
748 self
.tkconsole
.stderr
.write(s
)
750 def display_port_binding_error(self
):
751 tkMessageBox
.showerror(
752 "Port Binding Error",
753 "IDLE can't bind TCP/IP port 8833, which is necessary to "
754 "communicate with its Python execution server. Either "
755 "no networking is installed on this computer or another "
756 "process (another IDLE?) is using the port. Run IDLE with the -n "
757 "command line switch to start without a subprocess and refer to "
758 "Help/IDLE Help 'Running without a subprocess' for further "
760 master
=self
.tkconsole
.text
)
762 def display_no_subprocess_error(self
):
763 tkMessageBox
.showerror(
764 "Subprocess Startup Error",
765 "IDLE's subprocess didn't make connection. Either IDLE can't "
766 "start a subprocess or personal firewall software is blocking "
768 master
=self
.tkconsole
.text
)
770 def display_executing_dialog(self
):
771 tkMessageBox
.showerror(
773 "The Python Shell window is already executing a command; "
774 "please wait until it is finished.",
775 master
=self
.tkconsole
.text
)
778 class PyShell(OutputWindow
):
780 shell_title
= "Python Shell"
783 ColorDelegator
= ModifiedColorDelegator
784 UndoDelegator
= ModifiedUndoDelegator
791 ("options", "_Options"),
792 ("windows", "_Windows"),
796 if macosxSupport
.runningAsOSXApp():
798 menu_specs
[-2] = ("windows", "_Window")
802 from IdleHistory
import History
804 def __init__(self
, flist
=None):
807 if ms
[2][0] != "shell":
808 ms
.insert(2, ("shell", "She_ll"))
809 self
.interp
= ModifiedInterpreter(self
)
814 flist
= PyShellFileList(root
)
816 OutputWindow
.__init
__(self
, flist
, None, None)
818 ## self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
820 # indentwidth must be 8 when using tabs. See note in EditorWindow:
822 self
.context_use_ps1
= True
825 text
.configure(wrap
="char")
826 text
.bind("<<newline-and-indent>>", self
.enter_callback
)
827 text
.bind("<<plain-newline-and-indent>>", self
.linefeed_callback
)
828 text
.bind("<<interrupt-execution>>", self
.cancel_callback
)
829 text
.bind("<<end-of-file>>", self
.eof_callback
)
830 text
.bind("<<open-stack-viewer>>", self
.open_stack_viewer
)
831 text
.bind("<<toggle-debugger>>", self
.toggle_debugger
)
832 text
.bind("<<toggle-jit-stack-viewer>>", self
.toggle_jit_stack_viewer
)
834 text
.bind("<<view-restart>>", self
.view_restart_mark
)
835 text
.bind("<<restart-shell>>", self
.restart_shell
)
837 self
.save_stdout
= sys
.stdout
838 self
.save_stderr
= sys
.stderr
839 self
.save_stdin
= sys
.stdin
841 self
.stdout
= PseudoFile(self
, "stdout", IOBinding
.encoding
)
842 self
.stderr
= PseudoFile(self
, "stderr", IOBinding
.encoding
)
843 self
.console
= PseudoFile(self
, "console", IOBinding
.encoding
)
844 if not use_subprocess
:
845 sys
.stdout
= self
.stdout
846 sys
.stderr
= self
.stderr
849 self
.history
= self
.History(self
.text
)
851 self
.pollinterval
= 50 # millisec
853 def get_standard_extension_names(self
):
854 return idleConf
.GetExtensions(shell_only
=True)
862 def set_warning_stream(self
, stream
):
863 global warning_stream
864 warning_stream
= stream
866 def get_warning_stream(self
):
867 return warning_stream
869 def toggle_debugger(self
, event
=None):
871 tkMessageBox
.showerror("Don't debug now",
872 "You can only toggle the debugger when idle",
874 self
.set_debugger_indicator()
877 db
= self
.interp
.getdebugger()
879 self
.close_debugger()
883 def set_debugger_indicator(self
):
884 db
= self
.interp
.getdebugger()
885 self
.setvar("<<toggle-debugger>>", not not db
)
887 def toggle_jit_stack_viewer(self
, event
=None):
888 pass # All we need is the variable
890 def close_debugger(self
):
891 db
= self
.interp
.getdebugger()
893 self
.interp
.setdebugger(None)
895 if self
.interp
.rpcclt
:
896 RemoteDebugger
.close_remote_debugger(self
.interp
.rpcclt
)
898 self
.console
.write("[DEBUG OFF]\n")
901 self
.set_debugger_indicator()
903 def open_debugger(self
):
904 if self
.interp
.rpcclt
:
905 dbg_gui
= RemoteDebugger
.start_remote_debugger(self
.interp
.rpcclt
,
908 dbg_gui
= Debugger
.Debugger(self
)
909 self
.interp
.setdebugger(dbg_gui
)
910 dbg_gui
.load_breakpoints()
911 sys
.ps1
= "[DEBUG ON]\n>>> "
913 self
.set_debugger_indicator()
915 def beginexecuting(self
):
916 "Helper for ModifiedInterpreter"
920 def endexecuting(self
):
921 "Helper for ModifiedInterpreter"
927 "Extend EditorWindow.close()"
929 response
= tkMessageBox
.askokcancel(
931 "The program is still running!\n Do you want to kill it?",
934 if response
is False:
940 # Wait for poll_subprocess() rescheduling to stop
941 self
.text
.after(2 * self
.pollinterval
, self
.close2
)
944 return EditorWindow
.close(self
)
947 "Extend EditorWindow._close(), shut down debugger and execution server"
948 self
.close_debugger()
950 self
.interp
.kill_subprocess()
951 # Restore std streams
952 sys
.stdout
= self
.save_stdout
953 sys
.stderr
= self
.save_stderr
954 sys
.stdin
= self
.save_stdin
958 self
.flist
.pyshell
= None
960 EditorWindow
._close
(self
)
962 def ispythonsource(self
, filename
):
963 "Override EditorWindow method: never remove the colorizer"
966 def short_title(self
):
967 return self
.shell_title
970 'Type "copyright", "credits" or "license()" for more information.'
972 firewallmessage
= """
973 ****************************************************************
974 Personal firewall software may warn about the connection IDLE
975 makes to its subprocess using this computer's internal loopback
976 interface. This connection is not visible on any external
977 interface and no data is sent to or received from the Internet.
978 ****************************************************************
985 client
= self
.interp
.start_subprocess()
990 nosub
= "==== No Subprocess ===="
991 self
.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" %
992 (sys
.version
, sys
.platform
, self
.COPYRIGHT
,
993 self
.firewallmessage
, idlever
.IDLE_VERSION
, nosub
))
996 Tkinter
._default
_root
= None # 03Jan04 KBK What's this?
1003 self
.top
.mainloop() # nested mainloop()
1006 line
= self
.text
.get("iomark", "end-1c")
1007 if len(line
) == 0: # may be EOF if we quit our mainloop with Ctrl-C
1009 if isinstance(line
, unicode):
1012 line
= line
.encode(IOBinding
.encoding
)
1013 except UnicodeError:
1018 if not use_subprocess
:
1019 raise KeyboardInterrupt
1028 def cancel_callback(self
, event
=None):
1030 if self
.text
.compare("sel.first", "!=", "sel.last"):
1031 return # Active selection -- always use default binding
1034 if not (self
.executing
or self
.reading
):
1036 self
.interp
.write("KeyboardInterrupt\n")
1041 if (self
.executing
and self
.interp
.rpcclt
):
1042 if self
.interp
.getdebugger():
1043 self
.interp
.restart_subprocess()
1045 self
.interp
.interrupt_subprocess()
1047 self
.top
.quit() # exit the nested mainloop() in readline()
1050 def eof_callback(self
, event
):
1051 if self
.executing
and not self
.reading
:
1052 return # Let the default binding (delete next char) take over
1053 if not (self
.text
.compare("iomark", "==", "insert") and
1054 self
.text
.compare("insert", "==", "end-1c")):
1055 return # Let the default binding (delete next char) take over
1056 if not self
.executing
:
1065 def linefeed_callback(self
, event
):
1066 # Insert a linefeed without entering anything (still autoindented)
1068 self
.text
.insert("insert", "\n")
1069 self
.text
.see("insert")
1071 self
.newline_and_indent_event(event
)
1074 def enter_callback(self
, event
):
1075 if self
.executing
and not self
.reading
:
1076 return # Let the default binding (insert '\n') take over
1077 # If some text is selected, recall the selection
1078 # (but only if this before the I/O mark)
1080 sel
= self
.text
.get("sel.first", "sel.last")
1082 if self
.text
.compare("sel.last", "<=", "iomark"):
1083 self
.recall(sel
, event
)
1087 # If we're strictly before the line containing iomark, recall
1088 # the current line, less a leading prompt, less leading or
1089 # trailing whitespace
1090 if self
.text
.compare("insert", "<", "iomark linestart"):
1091 # Check if there's a relevant stdin range -- if so, use it
1092 prev
= self
.text
.tag_prevrange("stdin", "insert")
1093 if prev
and self
.text
.compare("insert", "<", prev
[1]):
1094 self
.recall(self
.text
.get(prev
[0], prev
[1]), event
)
1096 next
= self
.text
.tag_nextrange("stdin", "insert")
1097 if next
and self
.text
.compare("insert lineend", ">=", next
[0]):
1098 self
.recall(self
.text
.get(next
[0], next
[1]), event
)
1100 # No stdin mark -- just get the current line, less any prompt
1101 indices
= self
.text
.tag_nextrange("console", "insert linestart")
1103 self
.text
.compare(indices
[0], "<=", "insert linestart"):
1104 self
.recall(self
.text
.get(indices
[1], "insert lineend"), event
)
1106 self
.recall(self
.text
.get("insert linestart", "insert lineend"), event
)
1108 # If we're between the beginning of the line and the iomark, i.e.
1109 # in the prompt area, move to the end of the prompt
1110 if self
.text
.compare("insert", "<", "iomark"):
1111 self
.text
.mark_set("insert", "iomark")
1112 # If we're in the current input and there's only whitespace
1113 # beyond the cursor, erase that whitespace first
1114 s
= self
.text
.get("insert", "end-1c")
1115 if s
and not s
.strip():
1116 self
.text
.delete("insert", "end-1c")
1117 # If we're in the current input before its last line,
1118 # insert a newline right at the insert point
1119 if self
.text
.compare("insert", "<", "end-1c linestart"):
1120 self
.newline_and_indent_event(event
)
1122 # We're in the last line; append a newline and submit it
1123 self
.text
.mark_set("insert", "end-1c")
1125 self
.text
.insert("insert", "\n")
1126 self
.text
.see("insert")
1128 self
.newline_and_indent_event(event
)
1129 self
.text
.tag_add("stdin", "iomark", "end-1c")
1130 self
.text
.update_idletasks()
1132 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
1137 def recall(self
, s
, event
):
1138 # remove leading and trailing empty or whitespace lines
1139 s
= re
.sub(r
'^\s*\n', '' , s
)
1140 s
= re
.sub(r
'\n\s*$', '', s
)
1141 lines
= s
.split('\n')
1142 self
.text
.undo_block_start()
1144 self
.text
.tag_remove("sel", "1.0", "end")
1145 self
.text
.mark_set("insert", "end-1c")
1146 prefix
= self
.text
.get("insert linestart", "insert")
1147 if prefix
.rstrip().endswith(':'):
1148 self
.newline_and_indent_event(event
)
1149 prefix
= self
.text
.get("insert linestart", "insert")
1150 self
.text
.insert("insert", lines
[0].strip())
1152 orig_base_indent
= re
.search(r
'^([ \t]*)', lines
[0]).group(0)
1153 new_base_indent
= re
.search(r
'^([ \t]*)', prefix
).group(0)
1154 for line
in lines
[1:]:
1155 if line
.startswith(orig_base_indent
):
1156 # replace orig base indentation with new indentation
1157 line
= new_base_indent
+ line
[len(orig_base_indent
):]
1158 self
.text
.insert('insert', '\n'+line
.rstrip())
1160 self
.text
.see("insert")
1161 self
.text
.undo_block_stop()
1164 line
= self
.text
.get("iomark", "end-1c")
1165 # Strip off last newline and surrounding whitespace.
1166 # (To allow you to hit return twice to end a statement.)
1168 while i
> 0 and line
[i
-1] in " \t":
1170 if i
> 0 and line
[i
-1] == "\n":
1172 while i
> 0 and line
[i
-1] in " \t":
1175 more
= self
.interp
.runsource(line
)
1177 def open_stack_viewer(self
, event
=None):
1178 if self
.interp
.rpcclt
:
1179 return self
.interp
.remote_stack_viewer()
1183 tkMessageBox
.showerror("No stack trace",
1184 "There is no stack trace yet.\n"
1185 "(sys.last_traceback is not defined)",
1188 from StackViewer
import StackBrowser
1189 sv
= StackBrowser(self
.root
, self
.flist
)
1191 def view_restart_mark(self
, event
=None):
1192 self
.text
.see("iomark")
1193 self
.text
.see("restart")
1195 def restart_shell(self
, event
=None):
1196 self
.interp
.restart_subprocess()
1198 def showprompt(self
):
1204 self
.console
.write(s
)
1205 self
.text
.mark_set("insert", "end-1c")
1206 self
.set_line_and_column()
1207 self
.io
.reset_undo()
1209 def resetoutput(self
):
1210 source
= self
.text
.get("iomark", "end-1c")
1212 self
.history
.history_store(source
)
1213 if self
.text
.get("end-2c") != "\n":
1214 self
.text
.insert("end-1c", "\n")
1215 self
.text
.mark_set("iomark", "end-1c")
1216 self
.set_line_and_column()
1217 sys
.stdout
.softspace
= 0
1219 def write(self
, s
, tags
=()):
1221 self
.text
.mark_gravity("iomark", "right")
1222 OutputWindow
.write(self
, s
, tags
, "iomark")
1223 self
.text
.mark_gravity("iomark", "left")
1228 if not use_subprocess
:
1229 raise KeyboardInterrupt
1231 class PseudoFile(object):
1233 def __init__(self
, shell
, tags
, encoding
=None):
1237 self
.encoding
= encoding
1240 self
.shell
.write(s
, self
.tags
)
1242 def writelines(self
, l
):
1254 USAGE: idle [-deins] [-t title] [file]*
1255 idle [-dns] [-t title] (-c cmd | -r file) [arg]*
1256 idle [-dns] [-t title] - [arg]*
1258 -h print this help message and exit
1259 -n run IDLE without a subprocess (see Help/IDLE Help for details)
1261 The following options will override the IDLE 'settings' configuration:
1263 -e open an edit window
1264 -i open a shell window
1266 The following options imply -i and will open a shell:
1268 -c cmd run the command in a shell, or
1269 -r file run script from file
1271 -d enable the debugger
1272 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1273 -t title set title of shell window
1275 A default edit window will be bypassed when -c, -r, or - are used.
1277 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1282 Open an edit window or shell depending on IDLE's configuration.
1284 idle foo.py foobar.py
1285 Edit the files, also open a shell if configured to start with shell.
1287 idle -est "Baz" foo.py
1288 Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1289 window with the title "Baz".
1291 idle -c "import sys; print sys.argv" "foo"
1292 Open a shell window and run the command, passing "-c" in sys.argv[0]
1293 and "foo" in sys.argv[1].
1295 idle -d -s -r foo.py "Hello World"
1296 Open a shell window, run a startup script, enable the debugger, and
1297 run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1300 echo "import sys; print sys.argv" | idle - "foobar"
1301 Open a shell window, run the script piped in, passing '' in sys.argv[0]
1302 and "foobar" in sys.argv[1].
1306 global flist
, root
, use_subprocess
1308 use_subprocess
= True
1309 enable_shell
= False
1316 opts
, args
= getopt
.getopt(sys
.argv
[1:], "c:deihnr:st:")
1317 except getopt
.error
, msg
:
1318 sys
.stderr
.write("Error: %s\n" % str(msg
))
1319 sys
.stderr
.write(usage_msg
)
1331 sys
.stdout
.write(usage_msg
)
1336 use_subprocess
= False
1339 if os
.path
.isfile(script
):
1342 print "No script file: ", script
1349 PyShell
.shell_title
= a
1351 if args
and args
[0] == '-':
1352 cmd
= sys
.stdin
.read()
1354 # process sys.argv and sys.path:
1355 for i
in range(len(sys
.path
)):
1356 sys
.path
[i
] = os
.path
.abspath(sys
.path
[i
])
1357 if args
and args
[0] == '-':
1358 sys
.argv
= [''] + args
[1:]
1360 sys
.argv
= ['-c'] + args
1362 sys
.argv
= [script
] + args
1366 for filename
in args
:
1367 pathx
.append(os
.path
.dirname(filename
))
1369 dir = os
.path
.abspath(dir)
1370 if not dir in sys
.path
:
1371 sys
.path
.insert(0, dir)
1374 if not dir in sys
.path
:
1375 sys
.path
.insert(0, dir)
1376 # check the IDLE settings configuration (but command line overrides)
1377 edit_start
= idleConf
.GetOption('main', 'General',
1378 'editor-on-startup', type='bool')
1379 enable_edit
= enable_edit
or edit_start
1380 enable_shell
= enable_shell
or not edit_start
1381 # start editor and/or shell windows:
1382 root
= Tk(className
="Idle")
1386 flist
= PyShellFileList(root
)
1387 macosxSupport
.setupApp(root
, flist
)
1390 if not (cmd
or script
):
1391 for filename
in args
:
1392 flist
.open(filename
)
1396 shell
= flist
.open_shell()
1398 return # couldn't open shell
1400 if macosxSupport
.runningAsOSXApp() and flist
.dict:
1401 # On OSX: when the user has double-clicked on a file that causes
1402 # IDLE to be launched the shell window will open just in front of
1403 # the file she wants to see. Lower the interpreter window when
1404 # there are open files.
1407 shell
= flist
.pyshell
1408 # handle remaining options:
1410 shell
.open_debugger()
1412 filename
= os
.environ
.get("IDLESTARTUP") or \
1413 os
.environ
.get("PYTHONSTARTUP")
1414 if filename
and os
.path
.isfile(filename
):
1415 shell
.interp
.execfile(filename
)
1416 if shell
and cmd
or script
:
1417 shell
.interp
.runcommand("""if 1:
1421 \n""" % (sys
.argv
,))
1423 shell
.interp
.execsource(cmd
)
1425 shell
.interp
.prepend_syspath(script
)
1426 shell
.interp
.execfile(script
)
1431 if __name__
== "__main__":
1432 sys
.modules
['PyShell'] = sys
.modules
['__main__']