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"),
302 None: idleConf
.GetHighlight(theme
, "normal"),
305 class ModifiedUndoDelegator(UndoDelegator
):
306 "Extend base class: forbid insert/delete before the I/O mark"
308 def insert(self
, index
, chars
, tags
=None):
310 if self
.delegate
.compare(index
, "<", "iomark"):
315 UndoDelegator
.insert(self
, index
, chars
, tags
)
317 def delete(self
, index1
, index2
=None):
319 if self
.delegate
.compare(index1
, "<", "iomark"):
324 UndoDelegator
.delete(self
, index1
, index2
)
327 class MyRPCClient(rpc
.RPCClient
):
329 def handle_EOF(self
):
330 "Override the base class - just re-raise EOFError"
334 class ModifiedInterpreter(InteractiveInterpreter
):
336 def __init__(self
, tkconsole
):
337 self
.tkconsole
= tkconsole
338 locals = sys
.modules
['__main__'].__dict
__
339 InteractiveInterpreter
.__init
__(self
, locals=locals)
340 self
.save_warnings_filters
= None
341 self
.restarting
= False
342 self
.subprocess_arglist
= self
.build_subprocess_arglist()
348 def spawn_subprocess(self
):
349 args
= self
.subprocess_arglist
350 self
.rpcpid
= os
.spawnv(os
.P_NOWAIT
, sys
.executable
, args
)
352 def build_subprocess_arglist(self
):
353 w
= ['-W' + s
for s
in sys
.warnoptions
]
354 # Maybe IDLE is installed and is being accessed via sys.path,
355 # or maybe it's not installed and the idle.py script is being
356 # run from the IDLE source directory.
357 del_exitf
= idleConf
.GetOption('main', 'General', 'delete-exitfunc',
358 default
=False, type='bool')
359 if __name__
== 'idlelib.PyShell':
360 command
= "__import__('idlelib.run').run.main(%r)" % (del_exitf
,)
362 command
= "__import__('run').main(%r)" % (del_exitf
,)
363 if sys
.platform
[:3] == 'win' and ' ' in sys
.executable
:
364 # handle embedded space in path by quoting the argument
365 decorated_exec
= '"%s"' % sys
.executable
367 decorated_exec
= sys
.executable
368 return [decorated_exec
] + w
+ ["-c", command
, str(self
.port
)]
370 def start_subprocess(self
):
371 # spawning first avoids passing a listening socket to the subprocess
372 self
.spawn_subprocess()
373 #time.sleep(20) # test to simulate GUI not accepting connection
374 addr
= (LOCALHOST
, self
.port
)
375 # Idle starts listening for connection on localhost
379 self
.rpcclt
= MyRPCClient(addr
)
381 except socket
.error
, err
:
384 self
.display_port_binding_error()
386 # Accept the connection from the Python execution server
387 self
.rpcclt
.listening_sock
.settimeout(10)
390 except socket
.timeout
, err
:
391 self
.display_no_subprocess_error()
393 self
.rpcclt
.register("stdin", self
.tkconsole
)
394 self
.rpcclt
.register("stdout", self
.tkconsole
.stdout
)
395 self
.rpcclt
.register("stderr", self
.tkconsole
.stderr
)
396 self
.rpcclt
.register("flist", self
.tkconsole
.flist
)
397 self
.rpcclt
.register("linecache", linecache
)
398 self
.rpcclt
.register("interp", self
)
400 self
.poll_subprocess()
403 def restart_subprocess(self
):
406 self
.restarting
= True
407 # close only the subprocess debugger
408 debug
= self
.getdebugger()
411 # Only close subprocess debugger, don't unregister gui_adap!
412 RemoteDebugger
.close_subprocess_debugger(self
.rpcclt
)
415 # Kill subprocess, spawn a new one, accept connection.
417 self
.unix_terminate()
418 console
= self
.tkconsole
419 was_executing
= console
.executing
420 console
.executing
= False
421 self
.spawn_subprocess()
424 except socket
.timeout
, err
:
425 self
.display_no_subprocess_error()
428 # annotate restart in shell window and mark it
429 console
.text
.delete("iomark", "end-1c")
433 halfbar
= ((int(console
.width
) - 16) // 2) * '='
434 console
.write(halfbar
+ ' RESTART ' + halfbar
)
435 console
.text
.mark_set("restart", "end-1c")
436 console
.text
.mark_gravity("restart", "left")
438 # restart subprocess debugger
440 # Restarted debugger connects to current instance of debug GUI
441 gui
= RemoteDebugger
.restart_subprocess_debugger(self
.rpcclt
)
442 # reload remote debugger breakpoints for all PyShellEditWindows
443 debug
.load_breakpoints()
444 self
.restarting
= False
447 def __request_interrupt(self
):
448 self
.rpcclt
.remotecall("exec", "interrupt_the_server", (), {})
450 def interrupt_subprocess(self
):
451 threading
.Thread(target
=self
.__request
_interrupt
).start()
453 def kill_subprocess(self
):
456 except AttributeError: # no socket
458 self
.unix_terminate()
459 self
.tkconsole
.executing
= False
462 def unix_terminate(self
):
463 "UNIX: make sure subprocess is terminated and collect status"
464 if hasattr(os
, 'kill'):
466 os
.kill(self
.rpcpid
, SIGTERM
)
468 # process already terminated:
472 os
.waitpid(self
.rpcpid
, 0)
476 def transfer_path(self
):
477 self
.runcommand("""if 1:
485 def poll_subprocess(self
):
490 response
= clt
.pollresponse(self
.active_seq
, wait
=0.05)
491 except (EOFError, IOError, KeyboardInterrupt):
492 # lost connection or subprocess terminated itself, restart
493 # [the KBI is from rpc.SocketIO.handle_EOF()]
494 if self
.tkconsole
.closing
:
497 self
.restart_subprocess()
499 self
.tkconsole
.resetoutput()
500 self
.active_seq
= None
502 console
= self
.tkconsole
.console
505 print >>console
, repr(what
)
506 elif how
== "EXCEPTION":
507 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
508 self
.remote_stack_viewer()
510 errmsg
= "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
511 print >>sys
.__stderr
__, errmsg
, what
512 print >>console
, errmsg
, what
513 # we received a response to the currently active seq number:
515 self
.tkconsole
.endexecuting()
516 except AttributeError: # shell may have closed
519 if not self
.tkconsole
.closing
:
520 self
.tkconsole
.text
.after(self
.tkconsole
.pollinterval
,
521 self
.poll_subprocess
)
525 def setdebugger(self
, debugger
):
526 self
.debugger
= debugger
528 def getdebugger(self
):
531 def open_remote_stack_viewer(self
):
532 """Initiate the remote stack viewer from a separate thread.
534 This method is called from the subprocess, and by returning from this
535 method we allow the subprocess to unblock. After a bit the shell
536 requests the subprocess to open the remote stack viewer which returns a
537 static object looking at the last exceptiopn. It is queried through
541 self
.tkconsole
.text
.after(300, self
.remote_stack_viewer
)
544 def remote_stack_viewer(self
):
545 import RemoteObjectBrowser
546 oid
= self
.rpcclt
.remotequeue("exec", "stackviewer", ("flist",), {})
548 self
.tkconsole
.root
.bell()
550 item
= RemoteObjectBrowser
.StubObjectTreeItem(self
.rpcclt
, oid
)
551 from TreeWidget
import ScrolledCanvas
, TreeNode
552 top
= Toplevel(self
.tkconsole
.root
)
553 theme
= idleConf
.GetOption('main','Theme','name')
554 background
= idleConf
.GetHighlight(theme
, 'normal')['background']
555 sc
= ScrolledCanvas(top
, bg
=background
, highlightthickness
=0)
556 sc
.frame
.pack(expand
=1, fill
="both")
557 node
= TreeNode(sc
.canvas
, None, item
)
559 # XXX Should GC the remote tree when closing the window
563 def execsource(self
, source
):
564 "Like runsource() but assumes complete exec source"
565 filename
= self
.stuffsource(source
)
566 self
.execfile(filename
, source
)
568 def execfile(self
, filename
, source
=None):
569 "Execute an existing file"
571 source
= open(filename
, "r").read()
573 code
= compile(source
, filename
, "exec")
574 except (OverflowError, SyntaxError):
575 self
.tkconsole
.resetoutput()
576 tkerr
= self
.tkconsole
.stderr
577 print>>tkerr
, '*** Error in script or command!\n'
578 print>>tkerr
, 'Traceback (most recent call last):'
579 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
580 self
.tkconsole
.showprompt()
584 def runsource(self
, source
):
585 "Extend base class method: Stuff the source in the line cache first"
586 filename
= self
.stuffsource(source
)
588 self
.save_warnings_filters
= warnings
.filters
[:]
589 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
590 if isinstance(source
, types
.UnicodeType
):
593 source
= source
.encode(IOBinding
.encoding
)
595 self
.tkconsole
.resetoutput()
596 self
.write("Unsupported characters in input\n")
599 # InteractiveInterpreter.runsource() calls its runcode() method,
600 # which is overridden (see below)
601 return InteractiveInterpreter
.runsource(self
, source
, filename
)
603 if self
.save_warnings_filters
is not None:
604 warnings
.filters
[:] = self
.save_warnings_filters
605 self
.save_warnings_filters
= None
607 def stuffsource(self
, source
):
608 "Stuff source in the filename cache"
609 filename
= "<pyshell#%d>" % self
.gid
610 self
.gid
= self
.gid
+ 1
611 lines
= source
.split("\n")
612 linecache
.cache
[filename
] = len(source
)+1, 0, lines
, filename
615 def prepend_syspath(self
, filename
):
616 "Prepend sys.path with file's directory if not already included"
617 self
.runcommand("""if 1:
620 from os.path import dirname as _dirname
621 _dir = _dirname(_filename)
622 if not _dir in _sys.path:
623 _sys.path.insert(0, _dir)
624 del _filename, _sys, _dirname, _dir
627 def showsyntaxerror(self
, filename
=None):
628 """Extend base class method: Add Colorizing
630 Color the offending position instead of printing it and pointing at it
634 text
= self
.tkconsole
.text
635 stuff
= self
.unpackerror()
637 msg
, lineno
, offset
, line
= stuff
639 pos
= "iomark + %d chars" % (offset
-1)
641 pos
= "iomark linestart + %d lines + %d chars" % \
643 text
.tag_add("ERROR", pos
)
646 if char
and char
in IDENTCHARS
:
647 text
.tag_add("ERROR", pos
+ " wordstart", pos
)
648 self
.tkconsole
.resetoutput()
649 self
.write("SyntaxError: %s\n" % str(msg
))
651 self
.tkconsole
.resetoutput()
652 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
653 self
.tkconsole
.showprompt()
655 def unpackerror(self
):
656 type, value
, tb
= sys
.exc_info()
657 ok
= type is SyntaxError
660 msg
, (dummy_filename
, lineno
, offset
, line
) = value
666 return msg
, lineno
, offset
, line
670 def showtraceback(self
):
671 "Extend base class method to reset output properly"
672 self
.tkconsole
.resetoutput()
673 self
.checklinecache()
674 InteractiveInterpreter
.showtraceback(self
)
675 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
676 self
.tkconsole
.open_stack_viewer()
678 def checklinecache(self
):
681 if key
[:1] + key
[-1:] != "<>":
684 def runcommand(self
, code
):
685 "Run the code without invoking the debugger"
686 # The code better not raise an exception!
687 if self
.tkconsole
.executing
:
688 self
.display_executing_dialog()
691 self
.rpcclt
.remotequeue("exec", "runcode", (code
,), {})
693 exec code
in self
.locals
696 def runcode(self
, code
):
697 "Override base class method"
698 if self
.tkconsole
.executing
:
699 self
.interp
.restart_subprocess()
700 self
.checklinecache()
701 if self
.save_warnings_filters
is not None:
702 warnings
.filters
[:] = self
.save_warnings_filters
703 self
.save_warnings_filters
= None
704 debugger
= self
.debugger
706 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()"
733 self
.tkconsole
.endexecuting()
735 if not use_subprocess
:
737 self
.tkconsole
.endexecuting()
738 except AttributeError: # shell may have closed
742 "Override base class method"
743 self
.tkconsole
.stderr
.write(s
)
745 def display_port_binding_error(self
):
746 tkMessageBox
.showerror(
747 "Port Binding Error",
748 "IDLE can't bind TCP/IP port 8833, which is necessary to "
749 "communicate with its Python execution server. Either "
750 "no networking is installed on this computer or another "
751 "process (another IDLE?) is using the port. Run IDLE with the -n "
752 "command line switch to start without a subprocess and refer to "
753 "Help/IDLE Help 'Running without a subprocess' for further "
755 master
=self
.tkconsole
.text
)
757 def display_no_subprocess_error(self
):
758 tkMessageBox
.showerror(
759 "Subprocess Startup Error",
760 "IDLE's subprocess didn't make connection. Either IDLE can't "
761 "start a subprocess or personal firewall software is blocking "
763 master
=self
.tkconsole
.text
)
765 def display_executing_dialog(self
):
766 tkMessageBox
.showerror(
768 "The Python Shell window is already executing a command; "
769 "please wait until it is finished.",
770 master
=self
.tkconsole
.text
)
773 class PyShell(OutputWindow
):
775 shell_title
= "Python Shell"
778 ColorDelegator
= ModifiedColorDelegator
779 UndoDelegator
= ModifiedUndoDelegator
786 ("options", "_Options"),
787 ("windows", "_Windows"),
791 if macosxSupport
.runningAsOSXApp():
793 menu_specs
[-2] = ("windows", "_Window")
797 from IdleHistory
import History
799 def __init__(self
, flist
=None):
802 if ms
[2][0] != "shell":
803 ms
.insert(2, ("shell", "She_ll"))
804 self
.interp
= ModifiedInterpreter(self
)
809 flist
= PyShellFileList(root
)
811 OutputWindow
.__init
__(self
, flist
, None, None)
813 ## self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
815 # indentwidth must be 8 when using tabs. See note in EditorWindow:
817 self
.context_use_ps1
= True
820 text
.configure(wrap
="char")
821 text
.bind("<<newline-and-indent>>", self
.enter_callback
)
822 text
.bind("<<plain-newline-and-indent>>", self
.linefeed_callback
)
823 text
.bind("<<interrupt-execution>>", self
.cancel_callback
)
824 text
.bind("<<beginning-of-line>>", self
.home_callback
)
825 text
.bind("<<end-of-file>>", self
.eof_callback
)
826 text
.bind("<<open-stack-viewer>>", self
.open_stack_viewer
)
827 text
.bind("<<toggle-debugger>>", self
.toggle_debugger
)
828 text
.bind("<<toggle-jit-stack-viewer>>", self
.toggle_jit_stack_viewer
)
830 text
.bind("<<view-restart>>", self
.view_restart_mark
)
831 text
.bind("<<restart-shell>>", self
.restart_shell
)
833 self
.save_stdout
= sys
.stdout
834 self
.save_stderr
= sys
.stderr
835 self
.save_stdin
= sys
.stdin
837 self
.stdout
= PseudoFile(self
, "stdout", IOBinding
.encoding
)
838 self
.stderr
= PseudoFile(self
, "stderr", IOBinding
.encoding
)
839 self
.console
= PseudoFile(self
, "console", IOBinding
.encoding
)
840 if not use_subprocess
:
841 sys
.stdout
= self
.stdout
842 sys
.stderr
= self
.stderr
845 self
.history
= self
.History(self
.text
)
847 self
.pollinterval
= 50 # millisec
849 def get_standard_extension_names(self
):
850 return idleConf
.GetExtensions(shell_only
=True)
858 def set_warning_stream(self
, stream
):
859 global warning_stream
860 warning_stream
= stream
862 def get_warning_stream(self
):
863 return warning_stream
865 def toggle_debugger(self
, event
=None):
867 tkMessageBox
.showerror("Don't debug now",
868 "You can only toggle the debugger when idle",
870 self
.set_debugger_indicator()
873 db
= self
.interp
.getdebugger()
875 self
.close_debugger()
879 def set_debugger_indicator(self
):
880 db
= self
.interp
.getdebugger()
881 self
.setvar("<<toggle-debugger>>", not not db
)
883 def toggle_jit_stack_viewer(self
, event
=None):
884 pass # All we need is the variable
886 def close_debugger(self
):
887 db
= self
.interp
.getdebugger()
889 self
.interp
.setdebugger(None)
891 if self
.interp
.rpcclt
:
892 RemoteDebugger
.close_remote_debugger(self
.interp
.rpcclt
)
894 self
.console
.write("[DEBUG OFF]\n")
897 self
.set_debugger_indicator()
899 def open_debugger(self
):
900 if self
.interp
.rpcclt
:
901 dbg_gui
= RemoteDebugger
.start_remote_debugger(self
.interp
.rpcclt
,
904 dbg_gui
= Debugger
.Debugger(self
)
905 self
.interp
.setdebugger(dbg_gui
)
906 dbg_gui
.load_breakpoints()
907 sys
.ps1
= "[DEBUG ON]\n>>> "
909 self
.set_debugger_indicator()
911 def beginexecuting(self
):
912 "Helper for ModifiedInterpreter"
916 def endexecuting(self
):
917 "Helper for ModifiedInterpreter"
923 "Extend EditorWindow.close()"
925 response
= tkMessageBox
.askokcancel(
927 "The program is still running!\n Do you want to kill it?",
930 if response
== False:
936 # Wait for poll_subprocess() rescheduling to stop
937 self
.text
.after(2 * self
.pollinterval
, self
.close2
)
940 return EditorWindow
.close(self
)
943 "Extend EditorWindow._close(), shut down debugger and execution server"
944 self
.close_debugger()
946 self
.interp
.kill_subprocess()
947 # Restore std streams
948 sys
.stdout
= self
.save_stdout
949 sys
.stderr
= self
.save_stderr
950 sys
.stdin
= self
.save_stdin
954 self
.flist
.pyshell
= None
956 EditorWindow
._close
(self
)
958 def ispythonsource(self
, filename
):
959 "Override EditorWindow method: never remove the colorizer"
962 def short_title(self
):
963 return self
.shell_title
966 'Type "copyright", "credits" or "license()" for more information.'
968 firewallmessage
= """
969 ****************************************************************
970 Personal firewall software may warn about the connection IDLE
971 makes to its subprocess using this computer's internal loopback
972 interface. This connection is not visible on any external
973 interface and no data is sent to or received from the Internet.
974 ****************************************************************
981 client
= self
.interp
.start_subprocess()
986 nosub
= "==== No Subprocess ===="
987 self
.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" %
988 (sys
.version
, sys
.platform
, self
.COPYRIGHT
,
989 self
.firewallmessage
, idlever
.IDLE_VERSION
, nosub
))
992 Tkinter
._default
_root
= None # 03Jan04 KBK What's this?
999 self
.top
.mainloop() # nested mainloop()
1002 line
= self
.text
.get("iomark", "end-1c")
1003 if len(line
) == 0: # may be EOF if we quit our mainloop with Ctrl-C
1005 if isinstance(line
, unicode):
1008 line
= line
.encode(IOBinding
.encoding
)
1009 except UnicodeError:
1014 if not use_subprocess
:
1015 raise KeyboardInterrupt
1024 def cancel_callback(self
, event
=None):
1026 if self
.text
.compare("sel.first", "!=", "sel.last"):
1027 return # Active selection -- always use default binding
1030 if not (self
.executing
or self
.reading
):
1032 self
.interp
.write("KeyboardInterrupt\n")
1037 if (self
.executing
and self
.interp
.rpcclt
):
1038 if self
.interp
.getdebugger():
1039 self
.interp
.restart_subprocess()
1041 self
.interp
.interrupt_subprocess()
1043 self
.top
.quit() # exit the nested mainloop() in readline()
1046 def eof_callback(self
, event
):
1047 if self
.executing
and not self
.reading
:
1048 return # Let the default binding (delete next char) take over
1049 if not (self
.text
.compare("iomark", "==", "insert") and
1050 self
.text
.compare("insert", "==", "end-1c")):
1051 return # Let the default binding (delete next char) take over
1052 if not self
.executing
:
1061 def home_callback(self
, event
):
1062 if event
.state
!= 0 and event
.keysym
== "Home":
1063 return # <Modifier-Home>; fall back to class binding
1064 if self
.text
.compare("iomark", "<=", "insert") and \
1065 self
.text
.compare("insert linestart", "<=", "iomark"):
1066 self
.text
.mark_set("insert", "iomark")
1067 self
.text
.tag_remove("sel", "1.0", "end")
1068 self
.text
.see("insert")
1071 def linefeed_callback(self
, event
):
1072 # Insert a linefeed without entering anything (still autoindented)
1074 self
.text
.insert("insert", "\n")
1075 self
.text
.see("insert")
1077 self
.newline_and_indent_event(event
)
1080 def enter_callback(self
, event
):
1081 if self
.executing
and not self
.reading
:
1082 return # Let the default binding (insert '\n') take over
1083 # If some text is selected, recall the selection
1084 # (but only if this before the I/O mark)
1086 sel
= self
.text
.get("sel.first", "sel.last")
1088 if self
.text
.compare("sel.last", "<=", "iomark"):
1089 self
.recall(sel
, event
)
1093 # If we're strictly before the line containing iomark, recall
1094 # the current line, less a leading prompt, less leading or
1095 # trailing whitespace
1096 if self
.text
.compare("insert", "<", "iomark linestart"):
1097 # Check if there's a relevant stdin range -- if so, use it
1098 prev
= self
.text
.tag_prevrange("stdin", "insert")
1099 if prev
and self
.text
.compare("insert", "<", prev
[1]):
1100 self
.recall(self
.text
.get(prev
[0], prev
[1]), event
)
1102 next
= self
.text
.tag_nextrange("stdin", "insert")
1103 if next
and self
.text
.compare("insert lineend", ">=", next
[0]):
1104 self
.recall(self
.text
.get(next
[0], next
[1]), event
)
1106 # No stdin mark -- just get the current line, less any prompt
1107 indices
= self
.text
.tag_nextrange("console", "insert linestart")
1109 self
.text
.compare(indices
[0], "<=", "insert linestart"):
1110 self
.recall(self
.text
.get(indices
[1], "insert lineend"), event
)
1112 self
.recall(self
.text
.get("insert linestart", "insert lineend"), event
)
1114 # If we're between the beginning of the line and the iomark, i.e.
1115 # in the prompt area, move to the end of the prompt
1116 if self
.text
.compare("insert", "<", "iomark"):
1117 self
.text
.mark_set("insert", "iomark")
1118 # If we're in the current input and there's only whitespace
1119 # beyond the cursor, erase that whitespace first
1120 s
= self
.text
.get("insert", "end-1c")
1121 if s
and not s
.strip():
1122 self
.text
.delete("insert", "end-1c")
1123 # If we're in the current input before its last line,
1124 # insert a newline right at the insert point
1125 if self
.text
.compare("insert", "<", "end-1c linestart"):
1126 self
.newline_and_indent_event(event
)
1128 # We're in the last line; append a newline and submit it
1129 self
.text
.mark_set("insert", "end-1c")
1131 self
.text
.insert("insert", "\n")
1132 self
.text
.see("insert")
1134 self
.newline_and_indent_event(event
)
1135 self
.text
.tag_add("stdin", "iomark", "end-1c")
1136 self
.text
.update_idletasks()
1138 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
1143 def recall(self
, s
, event
):
1144 # remove leading and trailing empty or whitespace lines
1145 s
= re
.sub(r
'^\s*\n', '' , s
)
1146 s
= re
.sub(r
'\n\s*$', '', s
)
1147 lines
= s
.split('\n')
1148 self
.text
.undo_block_start()
1150 self
.text
.tag_remove("sel", "1.0", "end")
1151 self
.text
.mark_set("insert", "end-1c")
1152 prefix
= self
.text
.get("insert linestart", "insert")
1153 if prefix
.rstrip().endswith(':'):
1154 self
.newline_and_indent_event(event
)
1155 prefix
= self
.text
.get("insert linestart", "insert")
1156 self
.text
.insert("insert", lines
[0].strip())
1158 orig_base_indent
= re
.search(r
'^([ \t]*)', lines
[0]).group(0)
1159 new_base_indent
= re
.search(r
'^([ \t]*)', prefix
).group(0)
1160 for line
in lines
[1:]:
1161 if line
.startswith(orig_base_indent
):
1162 # replace orig base indentation with new indentation
1163 line
= new_base_indent
+ line
[len(orig_base_indent
):]
1164 self
.text
.insert('insert', '\n'+line
.rstrip())
1166 self
.text
.see("insert")
1167 self
.text
.undo_block_stop()
1170 line
= self
.text
.get("iomark", "end-1c")
1171 # Strip off last newline and surrounding whitespace.
1172 # (To allow you to hit return twice to end a statement.)
1174 while i
> 0 and line
[i
-1] in " \t":
1176 if i
> 0 and line
[i
-1] == "\n":
1178 while i
> 0 and line
[i
-1] in " \t":
1181 more
= self
.interp
.runsource(line
)
1183 def open_stack_viewer(self
, event
=None):
1184 if self
.interp
.rpcclt
:
1185 return self
.interp
.remote_stack_viewer()
1189 tkMessageBox
.showerror("No stack trace",
1190 "There is no stack trace yet.\n"
1191 "(sys.last_traceback is not defined)",
1194 from StackViewer
import StackBrowser
1195 sv
= StackBrowser(self
.root
, self
.flist
)
1197 def view_restart_mark(self
, event
=None):
1198 self
.text
.see("iomark")
1199 self
.text
.see("restart")
1201 def restart_shell(self
, event
=None):
1202 self
.interp
.restart_subprocess()
1204 def showprompt(self
):
1210 self
.console
.write(s
)
1211 self
.text
.mark_set("insert", "end-1c")
1212 self
.set_line_and_column()
1213 self
.io
.reset_undo()
1215 def resetoutput(self
):
1216 source
= self
.text
.get("iomark", "end-1c")
1218 self
.history
.history_store(source
)
1219 if self
.text
.get("end-2c") != "\n":
1220 self
.text
.insert("end-1c", "\n")
1221 self
.text
.mark_set("iomark", "end-1c")
1222 self
.set_line_and_column()
1223 sys
.stdout
.softspace
= 0
1225 def write(self
, s
, tags
=()):
1227 self
.text
.mark_gravity("iomark", "right")
1228 OutputWindow
.write(self
, s
, tags
, "iomark")
1229 self
.text
.mark_gravity("iomark", "left")
1234 if not use_subprocess
:
1235 raise KeyboardInterrupt
1237 class PseudoFile(object):
1239 def __init__(self
, shell
, tags
, encoding
=None):
1243 self
.encoding
= encoding
1246 self
.shell
.write(s
, self
.tags
)
1248 def writelines(self
, l
):
1260 USAGE: idle [-deins] [-t title] [file]*
1261 idle [-dns] [-t title] (-c cmd | -r file) [arg]*
1262 idle [-dns] [-t title] - [arg]*
1264 -h print this help message and exit
1265 -n run IDLE without a subprocess (see Help/IDLE Help for details)
1267 The following options will override the IDLE 'settings' configuration:
1269 -e open an edit window
1270 -i open a shell window
1272 The following options imply -i and will open a shell:
1274 -c cmd run the command in a shell, or
1275 -r file run script from file
1277 -d enable the debugger
1278 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1279 -t title set title of shell window
1281 A default edit window will be bypassed when -c, -r, or - are used.
1283 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1288 Open an edit window or shell depending on IDLE's configuration.
1290 idle foo.py foobar.py
1291 Edit the files, also open a shell if configured to start with shell.
1293 idle -est "Baz" foo.py
1294 Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1295 window with the title "Baz".
1297 idle -c "import sys; print sys.argv" "foo"
1298 Open a shell window and run the command, passing "-c" in sys.argv[0]
1299 and "foo" in sys.argv[1].
1301 idle -d -s -r foo.py "Hello World"
1302 Open a shell window, run a startup script, enable the debugger, and
1303 run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1306 echo "import sys; print sys.argv" | idle - "foobar"
1307 Open a shell window, run the script piped in, passing '' in sys.argv[0]
1308 and "foobar" in sys.argv[1].
1312 global flist
, root
, use_subprocess
1314 use_subprocess
= True
1315 enable_shell
= False
1322 opts
, args
= getopt
.getopt(sys
.argv
[1:], "c:deihnr:st:")
1323 except getopt
.error
, msg
:
1324 sys
.stderr
.write("Error: %s\n" % str(msg
))
1325 sys
.stderr
.write(usage_msg
)
1337 sys
.stdout
.write(usage_msg
)
1342 use_subprocess
= False
1345 if os
.path
.isfile(script
):
1348 print "No script file: ", script
1355 PyShell
.shell_title
= a
1357 if args
and args
[0] == '-':
1358 cmd
= sys
.stdin
.read()
1360 # process sys.argv and sys.path:
1361 for i
in range(len(sys
.path
)):
1362 sys
.path
[i
] = os
.path
.abspath(sys
.path
[i
])
1363 if args
and args
[0] == '-':
1364 sys
.argv
= [''] + args
[1:]
1366 sys
.argv
= ['-c'] + args
1368 sys
.argv
= [script
] + args
1372 for filename
in args
:
1373 pathx
.append(os
.path
.dirname(filename
))
1375 dir = os
.path
.abspath(dir)
1376 if not dir in sys
.path
:
1377 sys
.path
.insert(0, dir)
1380 if not dir in sys
.path
:
1381 sys
.path
.insert(0, dir)
1382 # check the IDLE settings configuration (but command line overrides)
1383 edit_start
= idleConf
.GetOption('main', 'General',
1384 'editor-on-startup', type='bool')
1385 enable_edit
= enable_edit
or edit_start
1386 enable_shell
= enable_shell
or not edit_start
1387 # start editor and/or shell windows:
1388 root
= Tk(className
="Idle")
1392 flist
= PyShellFileList(root
)
1393 macosxSupport
.setupApp(root
, flist
)
1396 if not (cmd
or script
):
1397 for filename
in args
:
1398 flist
.open(filename
)
1402 shell
= flist
.open_shell()
1404 return # couldn't open shell
1406 if macosxSupport
.runningAsOSXApp() and flist
.dict:
1407 # On OSX: when the user has double-clicked on a file that causes
1408 # IDLE to be launched the shell window will open just in front of
1409 # the file she wants to see. Lower the interpreter window when
1410 # there are open files.
1413 shell
= flist
.pyshell
1414 # handle remaining options:
1416 shell
.open_debugger()
1418 filename
= os
.environ
.get("IDLESTARTUP") or \
1419 os
.environ
.get("PYTHONSTARTUP")
1420 if filename
and os
.path
.isfile(filename
):
1421 shell
.interp
.execfile(filename
)
1422 if shell
and cmd
or script
:
1423 shell
.interp
.runcommand("""if 1:
1427 \n""" % (sys
.argv
,))
1429 shell
.interp
.execsource(cmd
)
1431 shell
.interp
.prepend_syspath(script
)
1432 shell
.interp
.execfile(script
)
1437 if __name__
== "__main__":
1438 sys
.modules
['PyShell'] = sys
.modules
['__main__']