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 HOST
= '127.0.0.1' # python execution server on localhost loopback
41 PORT
= 0 # someday pass in host, port for remote debug capability
44 from signal
import SIGTERM
48 # Override warnings module to write to warning_stream. Initialize to send IDLE
49 # internal warnings to the console. ScriptBinding.check_syntax() will
50 # temporarily redirect the stream to the shell window to display warnings when
51 # checking user's code.
53 warning_stream
= sys
.__stderr
__
59 def idle_showwarning(message
, category
, filename
, lineno
,
60 file=None, line
=None):
64 file.write(warnings
.formatwarning(message
, category
, filename
,
65 lineno
, file=file, line
=line
))
67 pass ## file (probably __stderr__) is invalid, warning dropped.
68 warnings
.showwarning
= idle_showwarning
69 def idle_formatwarning(message
, category
, filename
, lineno
, line
=None):
70 """Format warnings the IDLE way"""
71 s
= "\nWarning (from warnings module):\n"
72 s
+= ' File \"%s\", line %s\n' % (filename
, lineno
)
74 line
= linecache
.getline(filename
, lineno
)
78 s
+= "%s: %s\n>>> " % (category
.__name
__, message
)
80 warnings
.formatwarning
= idle_formatwarning
82 def extended_linecache_checkcache(filename
=None,
83 orig_checkcache
=linecache
.checkcache
):
84 """Extend linecache.checkcache to preserve the <pyshell#...> entries
86 Rather than repeating the linecache code, patch it to save the
87 <pyshell#...> entries, call the original linecache.checkcache()
88 (skipping them), and then restore the saved entries.
90 orig_checkcache is bound at definition time to the original
91 method, allowing it to be patched.
93 cache
= linecache
.cache
95 for key
in list(cache
):
96 if key
[:1] + key
[-1:] == '<>':
97 save
[key
] = cache
.pop(key
)
98 orig_checkcache(filename
)
101 # Patch linecache.checkcache():
102 linecache
.checkcache
= extended_linecache_checkcache
105 class PyShellEditorWindow(EditorWindow
):
106 "Regular text edit window in IDLE, supports breakpoints"
108 def __init__(self
, *args
):
109 self
.breakpoints
= []
110 EditorWindow
.__init
__(self
, *args
)
111 self
.text
.bind("<<set-breakpoint-here>>", self
.set_breakpoint_here
)
112 self
.text
.bind("<<clear-breakpoint-here>>", self
.clear_breakpoint_here
)
113 self
.text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
115 self
.breakpointPath
= os
.path
.join(idleConf
.GetUserCfgDir(),
117 # whenever a file is changed, restore breakpoints
118 if self
.io
.filename
: self
.restore_file_breaks()
119 def filename_changed_hook(old_hook
=self
.io
.filename_change_hook
,
121 self
.restore_file_breaks()
123 self
.io
.set_filename_change_hook(filename_changed_hook
)
125 rmenu_specs
= [("Set Breakpoint", "<<set-breakpoint-here>>"),
126 ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
128 def set_breakpoint(self
, lineno
):
130 filename
= self
.io
.filename
131 text
.tag_add("BREAK", "%d.0" % lineno
, "%d.0" % (lineno
+1))
133 i
= self
.breakpoints
.index(lineno
)
134 except ValueError: # only add if missing, i.e. do once
135 self
.breakpoints
.append(lineno
)
136 try: # update the subprocess debugger
137 debug
= self
.flist
.pyshell
.interp
.debugger
138 debug
.set_breakpoint_here(filename
, lineno
)
139 except: # but debugger may not be active right now....
142 def set_breakpoint_here(self
, event
=None):
144 filename
= self
.io
.filename
148 lineno
= int(float(text
.index("insert")))
149 self
.set_breakpoint(lineno
)
151 def clear_breakpoint_here(self
, event
=None):
153 filename
= self
.io
.filename
157 lineno
= int(float(text
.index("insert")))
159 self
.breakpoints
.remove(lineno
)
162 text
.tag_remove("BREAK", "insert linestart",\
163 "insert lineend +1char")
165 debug
= self
.flist
.pyshell
.interp
.debugger
166 debug
.clear_breakpoint_here(filename
, lineno
)
170 def clear_file_breaks(self
):
173 filename
= self
.io
.filename
177 self
.breakpoints
= []
178 text
.tag_remove("BREAK", "1.0", END
)
180 debug
= self
.flist
.pyshell
.interp
.debugger
181 debug
.clear_file_breaks(filename
)
185 def store_file_breaks(self
):
186 "Save breakpoints when file is saved"
187 # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
188 # be run. The breaks are saved at that time. If we introduce
189 # a temporary file save feature the save breaks functionality
190 # needs to be re-verified, since the breaks at the time the
191 # temp file is created may differ from the breaks at the last
192 # permanent save of the file. Currently, a break introduced
193 # after a save will be effective, but not persistent.
194 # This is necessary to keep the saved breaks synched with the
197 # Breakpoints are set as tagged ranges in the text. Certain
198 # kinds of edits cause these ranges to be deleted: Inserting
199 # or deleting a line just before a breakpoint, and certain
200 # deletions prior to a breakpoint. These issues need to be
201 # investigated and understood. It's not clear if they are
202 # Tk issues or IDLE issues, or whether they can actually
203 # be fixed. Since a modified file has to be saved before it is
204 # run, and since self.breakpoints (from which the subprocess
205 # debugger is loaded) is updated during the save, the visible
206 # breaks stay synched with the subprocess even if one of these
207 # unexpected breakpoint deletions occurs.
208 breaks
= self
.breakpoints
209 filename
= self
.io
.filename
211 lines
= open(self
.breakpointPath
,"r").readlines()
214 new_file
= open(self
.breakpointPath
,"w")
216 if not line
.startswith(filename
+ '='):
218 self
.update_breakpoints()
219 breaks
= self
.breakpoints
221 new_file
.write(filename
+ '=' + str(breaks
) + '\n')
224 def restore_file_breaks(self
):
225 self
.text
.update() # this enables setting "BREAK" tags to be visible
226 filename
= self
.io
.filename
229 if os
.path
.isfile(self
.breakpointPath
):
230 lines
= open(self
.breakpointPath
,"r").readlines()
232 if line
.startswith(filename
+ '='):
233 breakpoint_linenumbers
= eval(line
[len(filename
)+1:])
234 for breakpoint_linenumber
in breakpoint_linenumbers
:
235 self
.set_breakpoint(breakpoint_linenumber
)
237 def update_breakpoints(self
):
238 "Retrieves all the breakpoints in the current window"
240 ranges
= text
.tag_ranges("BREAK")
241 linenumber_list
= self
.ranges_to_linenumbers(ranges
)
242 self
.breakpoints
= linenumber_list
244 def ranges_to_linenumbers(self
, ranges
):
246 for index
in range(0, len(ranges
), 2):
247 lineno
= int(float(ranges
[index
]))
248 end
= int(float(ranges
[index
+1]))
254 # XXX 13 Dec 2002 KBK Not used currently
255 # def saved_change_hook(self):
256 # "Extend base method - clear breaks if module is modified"
257 # if not self.get_saved():
258 # self.clear_file_breaks()
259 # EditorWindow.saved_change_hook(self)
262 "Extend base method - clear breaks when module is closed"
263 self
.clear_file_breaks()
264 EditorWindow
._close
(self
)
267 class PyShellFileList(FileList
):
268 "Extend base class: IDLE supports a shell and breakpoints"
270 # override FileList's class variable, instances return PyShellEditorWindow
271 # instead of EditorWindow when new edit windows are created.
272 EditorWindow
= PyShellEditorWindow
276 def open_shell(self
, event
=None):
278 self
.pyshell
.top
.wakeup()
280 self
.pyshell
= PyShell(self
)
282 if not self
.pyshell
.begin():
287 class ModifiedColorDelegator(ColorDelegator
):
288 "Extend base class: colorizer for the shell window itself"
291 ColorDelegator
.__init
__(self
)
294 def recolorize_main(self
):
295 self
.tag_remove("TODO", "1.0", "iomark")
296 self
.tag_add("SYNC", "1.0", "iomark")
297 ColorDelegator
.recolorize_main(self
)
299 def LoadTagDefs(self
):
300 ColorDelegator
.LoadTagDefs(self
)
301 theme
= idleConf
.GetOption('main','Theme','name')
302 self
.tagdefs
.update({
303 "stdin": {'background':None,'foreground':None},
304 "stdout": idleConf
.GetHighlight(theme
, "stdout"),
305 "stderr": idleConf
.GetHighlight(theme
, "stderr"),
306 "console": idleConf
.GetHighlight(theme
, "console"),
309 class ModifiedUndoDelegator(UndoDelegator
):
310 "Extend base class: forbid insert/delete before the I/O mark"
312 def insert(self
, index
, chars
, tags
=None):
314 if self
.delegate
.compare(index
, "<", "iomark"):
319 UndoDelegator
.insert(self
, index
, chars
, tags
)
321 def delete(self
, index1
, index2
=None):
323 if self
.delegate
.compare(index1
, "<", "iomark"):
328 UndoDelegator
.delete(self
, index1
, index2
)
331 class MyRPCClient(rpc
.RPCClient
):
333 def handle_EOF(self
):
334 "Override the base class - just re-raise EOFError"
338 class ModifiedInterpreter(InteractiveInterpreter
):
340 def __init__(self
, tkconsole
):
341 self
.tkconsole
= tkconsole
342 locals = sys
.modules
['__main__'].__dict
__
343 InteractiveInterpreter
.__init
__(self
, locals=locals)
344 self
.save_warnings_filters
= None
345 self
.restarting
= False
346 self
.subprocess_arglist
= None
352 def spawn_subprocess(self
):
353 if self
.subprocess_arglist
== None:
354 self
.subprocess_arglist
= self
.build_subprocess_arglist()
355 args
= self
.subprocess_arglist
356 self
.rpcpid
= os
.spawnv(os
.P_NOWAIT
, sys
.executable
, args
)
358 def build_subprocess_arglist(self
):
359 assert (self
.port
!=0), (
360 "Socket should have been assigned a port number.")
361 w
= ['-W' + s
for s
in sys
.warnoptions
]
362 if 1/2 > 0: # account for new division
364 # Maybe IDLE is installed and is being accessed via sys.path,
365 # or maybe it's not installed and the idle.py script is being
366 # run from the IDLE source directory.
367 del_exitf
= idleConf
.GetOption('main', 'General', 'delete-exitfunc',
368 default
=False, type='bool')
369 if __name__
== 'idlelib.PyShell':
370 command
= "__import__('idlelib.run').run.main(%r)" % (del_exitf
,)
372 command
= "__import__('run').main(%r)" % (del_exitf
,)
373 if sys
.platform
[:3] == 'win' and ' ' in sys
.executable
:
374 # handle embedded space in path by quoting the argument
375 decorated_exec
= '"%s"' % sys
.executable
377 decorated_exec
= sys
.executable
378 return [decorated_exec
] + w
+ ["-c", command
, str(self
.port
)]
380 def start_subprocess(self
):
381 addr
= (HOST
, self
.port
)
382 # GUI makes several attempts to acquire socket, listens for connection
386 self
.rpcclt
= MyRPCClient(addr
)
388 except socket
.error
, err
:
391 self
.display_port_binding_error()
393 # if PORT was 0, system will assign an 'ephemeral' port. Find it out:
394 self
.port
= self
.rpcclt
.listening_sock
.getsockname()[1]
395 # if PORT was not 0, probably working with a remote execution server
397 # To allow reconnection within the 2MSL wait (cf. Stevens TCP
398 # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic
399 # on Windows since the implementation allows two active sockets on
401 self
.rpcclt
.listening_sock
.setsockopt(socket
.SOL_SOCKET
,
402 socket
.SO_REUSEADDR
, 1)
403 self
.spawn_subprocess()
404 #time.sleep(20) # test to simulate GUI not accepting connection
405 # Accept the connection from the Python execution server
406 self
.rpcclt
.listening_sock
.settimeout(10)
409 except socket
.timeout
, err
:
410 self
.display_no_subprocess_error()
412 self
.rpcclt
.register("stdin", self
.tkconsole
)
413 self
.rpcclt
.register("stdout", self
.tkconsole
.stdout
)
414 self
.rpcclt
.register("stderr", self
.tkconsole
.stderr
)
415 self
.rpcclt
.register("flist", self
.tkconsole
.flist
)
416 self
.rpcclt
.register("linecache", linecache
)
417 self
.rpcclt
.register("interp", self
)
419 self
.poll_subprocess()
422 def restart_subprocess(self
):
425 self
.restarting
= True
426 # close only the subprocess debugger
427 debug
= self
.getdebugger()
430 # Only close subprocess debugger, don't unregister gui_adap!
431 RemoteDebugger
.close_subprocess_debugger(self
.rpcclt
)
434 # Kill subprocess, spawn a new one, accept connection.
436 self
.unix_terminate()
437 console
= self
.tkconsole
438 was_executing
= console
.executing
439 console
.executing
= False
440 self
.spawn_subprocess()
443 except socket
.timeout
, err
:
444 self
.display_no_subprocess_error()
447 # annotate restart in shell window and mark it
448 console
.text
.delete("iomark", "end-1c")
452 halfbar
= ((int(console
.width
) - 16) // 2) * '='
453 console
.write(halfbar
+ ' RESTART ' + halfbar
)
454 console
.text
.mark_set("restart", "end-1c")
455 console
.text
.mark_gravity("restart", "left")
457 # restart subprocess debugger
459 # Restarted debugger connects to current instance of debug GUI
460 gui
= RemoteDebugger
.restart_subprocess_debugger(self
.rpcclt
)
461 # reload remote debugger breakpoints for all PyShellEditWindows
462 debug
.load_breakpoints()
463 self
.restarting
= False
466 def __request_interrupt(self
):
467 self
.rpcclt
.remotecall("exec", "interrupt_the_server", (), {})
469 def interrupt_subprocess(self
):
470 threading
.Thread(target
=self
.__request
_interrupt
).start()
472 def kill_subprocess(self
):
475 except AttributeError: # no socket
477 self
.unix_terminate()
478 self
.tkconsole
.executing
= False
481 def unix_terminate(self
):
482 "UNIX: make sure subprocess is terminated and collect status"
483 if hasattr(os
, 'kill'):
485 os
.kill(self
.rpcpid
, SIGTERM
)
487 # process already terminated:
491 os
.waitpid(self
.rpcpid
, 0)
495 def transfer_path(self
):
496 self
.runcommand("""if 1:
504 def poll_subprocess(self
):
509 response
= clt
.pollresponse(self
.active_seq
, wait
=0.05)
510 except (EOFError, IOError, KeyboardInterrupt):
511 # lost connection or subprocess terminated itself, restart
512 # [the KBI is from rpc.SocketIO.handle_EOF()]
513 if self
.tkconsole
.closing
:
516 self
.restart_subprocess()
518 self
.tkconsole
.resetoutput()
519 self
.active_seq
= None
521 console
= self
.tkconsole
.console
524 print >>console
, repr(what
)
525 elif how
== "EXCEPTION":
526 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
527 self
.remote_stack_viewer()
529 errmsg
= "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
530 print >>sys
.__stderr
__, errmsg
, what
531 print >>console
, errmsg
, what
532 # we received a response to the currently active seq number:
534 self
.tkconsole
.endexecuting()
535 except AttributeError: # shell may have closed
538 if not self
.tkconsole
.closing
:
539 self
.tkconsole
.text
.after(self
.tkconsole
.pollinterval
,
540 self
.poll_subprocess
)
544 def setdebugger(self
, debugger
):
545 self
.debugger
= debugger
547 def getdebugger(self
):
550 def open_remote_stack_viewer(self
):
551 """Initiate the remote stack viewer from a separate thread.
553 This method is called from the subprocess, and by returning from this
554 method we allow the subprocess to unblock. After a bit the shell
555 requests the subprocess to open the remote stack viewer which returns a
556 static object looking at the last exceptiopn. It is queried through
560 self
.tkconsole
.text
.after(300, self
.remote_stack_viewer
)
563 def remote_stack_viewer(self
):
564 import RemoteObjectBrowser
565 oid
= self
.rpcclt
.remotequeue("exec", "stackviewer", ("flist",), {})
567 self
.tkconsole
.root
.bell()
569 item
= RemoteObjectBrowser
.StubObjectTreeItem(self
.rpcclt
, oid
)
570 from TreeWidget
import ScrolledCanvas
, TreeNode
571 top
= Toplevel(self
.tkconsole
.root
)
572 theme
= idleConf
.GetOption('main','Theme','name')
573 background
= idleConf
.GetHighlight(theme
, 'normal')['background']
574 sc
= ScrolledCanvas(top
, bg
=background
, highlightthickness
=0)
575 sc
.frame
.pack(expand
=1, fill
="both")
576 node
= TreeNode(sc
.canvas
, None, item
)
578 # XXX Should GC the remote tree when closing the window
582 def execsource(self
, source
):
583 "Like runsource() but assumes complete exec source"
584 filename
= self
.stuffsource(source
)
585 self
.execfile(filename
, source
)
587 def execfile(self
, filename
, source
=None):
588 "Execute an existing file"
590 source
= open(filename
, "r").read()
592 code
= compile(source
, filename
, "exec")
593 except (OverflowError, SyntaxError):
594 self
.tkconsole
.resetoutput()
595 tkerr
= self
.tkconsole
.stderr
596 print>>tkerr
, '*** Error in script or command!\n'
597 print>>tkerr
, 'Traceback (most recent call last):'
598 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
599 self
.tkconsole
.showprompt()
603 def runsource(self
, source
):
604 "Extend base class method: Stuff the source in the line cache first"
605 filename
= self
.stuffsource(source
)
607 self
.save_warnings_filters
= warnings
.filters
[:]
608 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
609 if isinstance(source
, types
.UnicodeType
):
612 source
= source
.encode(IOBinding
.encoding
)
614 self
.tkconsole
.resetoutput()
615 self
.write("Unsupported characters in input\n")
618 # InteractiveInterpreter.runsource() calls its runcode() method,
619 # which is overridden (see below)
620 return InteractiveInterpreter
.runsource(self
, source
, filename
)
622 if self
.save_warnings_filters
is not None:
623 warnings
.filters
[:] = self
.save_warnings_filters
624 self
.save_warnings_filters
= None
626 def stuffsource(self
, source
):
627 "Stuff source in the filename cache"
628 filename
= "<pyshell#%d>" % self
.gid
629 self
.gid
= self
.gid
+ 1
630 lines
= source
.split("\n")
631 linecache
.cache
[filename
] = len(source
)+1, 0, lines
, filename
634 def prepend_syspath(self
, filename
):
635 "Prepend sys.path with file's directory if not already included"
636 self
.runcommand("""if 1:
639 from os.path import dirname as _dirname
640 _dir = _dirname(_filename)
641 if not _dir in _sys.path:
642 _sys.path.insert(0, _dir)
643 del _filename, _sys, _dirname, _dir
646 def showsyntaxerror(self
, filename
=None):
647 """Extend base class method: Add Colorizing
649 Color the offending position instead of printing it and pointing at it
653 text
= self
.tkconsole
.text
654 stuff
= self
.unpackerror()
656 msg
, lineno
, offset
, line
= stuff
658 pos
= "iomark + %d chars" % (offset
-1)
660 pos
= "iomark linestart + %d lines + %d chars" % \
662 text
.tag_add("ERROR", pos
)
665 if char
and char
in IDENTCHARS
:
666 text
.tag_add("ERROR", pos
+ " wordstart", pos
)
667 self
.tkconsole
.resetoutput()
668 self
.write("SyntaxError: %s\n" % str(msg
))
670 self
.tkconsole
.resetoutput()
671 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
672 self
.tkconsole
.showprompt()
674 def unpackerror(self
):
675 type, value
, tb
= sys
.exc_info()
676 ok
= type is SyntaxError
679 msg
, (dummy_filename
, lineno
, offset
, line
) = value
685 return msg
, lineno
, offset
, line
689 def showtraceback(self
):
690 "Extend base class method to reset output properly"
691 self
.tkconsole
.resetoutput()
692 self
.checklinecache()
693 InteractiveInterpreter
.showtraceback(self
)
694 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
695 self
.tkconsole
.open_stack_viewer()
697 def checklinecache(self
):
700 if key
[:1] + key
[-1:] != "<>":
703 def runcommand(self
, code
):
704 "Run the code without invoking the debugger"
705 # The code better not raise an exception!
706 if self
.tkconsole
.executing
:
707 self
.display_executing_dialog()
710 self
.rpcclt
.remotequeue("exec", "runcode", (code
,), {})
712 exec code
in self
.locals
715 def runcode(self
, code
):
716 "Override base class method"
717 if self
.tkconsole
.executing
:
718 self
.interp
.restart_subprocess()
719 self
.checklinecache()
720 if self
.save_warnings_filters
is not None:
721 warnings
.filters
[:] = self
.save_warnings_filters
722 self
.save_warnings_filters
= None
723 debugger
= self
.debugger
725 self
.tkconsole
.beginexecuting()
726 if not debugger
and self
.rpcclt
is not None:
727 self
.active_seq
= self
.rpcclt
.asyncqueue("exec", "runcode",
730 debugger
.run(code
, self
.locals)
732 exec code
in self
.locals
734 if not self
.tkconsole
.closing
:
735 if tkMessageBox
.askyesno(
737 "Do you want to exit altogether?",
739 master
=self
.tkconsole
.text
):
747 print >>self
.tkconsole
.stderr
, \
748 "IDLE internal error in runcode()"
750 self
.tkconsole
.endexecuting()
752 if self
.tkconsole
.canceled
:
753 self
.tkconsole
.canceled
= False
754 print >>self
.tkconsole
.stderr
, "KeyboardInterrupt"
758 if not use_subprocess
:
760 self
.tkconsole
.endexecuting()
761 except AttributeError: # shell may have closed
765 "Override base class method"
766 self
.tkconsole
.stderr
.write(s
)
768 def display_port_binding_error(self
):
769 tkMessageBox
.showerror(
770 "Port Binding Error",
771 "IDLE can't bind to a TCP/IP port, which is necessary to "
772 "communicate with its Python execution server. This might be "
773 "because no networking is installed on this computer. "
774 "Run IDLE with the -n command line switch to start without a "
775 "subprocess and refer to Help/IDLE Help 'Running without a "
776 "subprocess' for further details.",
777 master
=self
.tkconsole
.text
)
779 def display_no_subprocess_error(self
):
780 tkMessageBox
.showerror(
781 "Subprocess Startup Error",
782 "IDLE's subprocess didn't make connection. Either IDLE can't "
783 "start a subprocess or personal firewall software is blocking "
785 master
=self
.tkconsole
.text
)
787 def display_executing_dialog(self
):
788 tkMessageBox
.showerror(
790 "The Python Shell window is already executing a command; "
791 "please wait until it is finished.",
792 master
=self
.tkconsole
.text
)
795 class PyShell(OutputWindow
):
797 shell_title
= "Python Shell"
800 ColorDelegator
= ModifiedColorDelegator
801 UndoDelegator
= ModifiedUndoDelegator
808 ("options", "_Options"),
809 ("windows", "_Windows"),
813 if macosxSupport
.runningAsOSXApp():
815 menu_specs
[-2] = ("windows", "_Window")
819 from IdleHistory
import History
821 def __init__(self
, flist
=None):
824 if ms
[2][0] != "shell":
825 ms
.insert(2, ("shell", "She_ll"))
826 self
.interp
= ModifiedInterpreter(self
)
831 flist
= PyShellFileList(root
)
833 OutputWindow
.__init
__(self
, flist
, None, None)
835 ## self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
837 # indentwidth must be 8 when using tabs. See note in EditorWindow:
839 self
.context_use_ps1
= True
842 text
.configure(wrap
="char")
843 text
.bind("<<newline-and-indent>>", self
.enter_callback
)
844 text
.bind("<<plain-newline-and-indent>>", self
.linefeed_callback
)
845 text
.bind("<<interrupt-execution>>", self
.cancel_callback
)
846 text
.bind("<<end-of-file>>", self
.eof_callback
)
847 text
.bind("<<open-stack-viewer>>", self
.open_stack_viewer
)
848 text
.bind("<<toggle-debugger>>", self
.toggle_debugger
)
849 text
.bind("<<toggle-jit-stack-viewer>>", self
.toggle_jit_stack_viewer
)
851 text
.bind("<<view-restart>>", self
.view_restart_mark
)
852 text
.bind("<<restart-shell>>", self
.restart_shell
)
854 self
.save_stdout
= sys
.stdout
855 self
.save_stderr
= sys
.stderr
856 self
.save_stdin
= sys
.stdin
858 self
.stdout
= PseudoFile(self
, "stdout", IOBinding
.encoding
)
859 self
.stderr
= PseudoFile(self
, "stderr", IOBinding
.encoding
)
860 self
.console
= PseudoFile(self
, "console", IOBinding
.encoding
)
861 if not use_subprocess
:
862 sys
.stdout
= self
.stdout
863 sys
.stderr
= self
.stderr
866 self
.history
= self
.History(self
.text
)
868 self
.pollinterval
= 50 # millisec
870 def get_standard_extension_names(self
):
871 return idleConf
.GetExtensions(shell_only
=True)
879 def set_warning_stream(self
, stream
):
880 global warning_stream
881 warning_stream
= stream
883 def get_warning_stream(self
):
884 return warning_stream
886 def toggle_debugger(self
, event
=None):
888 tkMessageBox
.showerror("Don't debug now",
889 "You can only toggle the debugger when idle",
891 self
.set_debugger_indicator()
894 db
= self
.interp
.getdebugger()
896 self
.close_debugger()
900 def set_debugger_indicator(self
):
901 db
= self
.interp
.getdebugger()
902 self
.setvar("<<toggle-debugger>>", not not db
)
904 def toggle_jit_stack_viewer(self
, event
=None):
905 pass # All we need is the variable
907 def close_debugger(self
):
908 db
= self
.interp
.getdebugger()
910 self
.interp
.setdebugger(None)
912 if self
.interp
.rpcclt
:
913 RemoteDebugger
.close_remote_debugger(self
.interp
.rpcclt
)
915 self
.console
.write("[DEBUG OFF]\n")
918 self
.set_debugger_indicator()
920 def open_debugger(self
):
921 if self
.interp
.rpcclt
:
922 dbg_gui
= RemoteDebugger
.start_remote_debugger(self
.interp
.rpcclt
,
925 dbg_gui
= Debugger
.Debugger(self
)
926 self
.interp
.setdebugger(dbg_gui
)
927 dbg_gui
.load_breakpoints()
928 sys
.ps1
= "[DEBUG ON]\n>>> "
930 self
.set_debugger_indicator()
932 def beginexecuting(self
):
933 "Helper for ModifiedInterpreter"
937 def endexecuting(self
):
938 "Helper for ModifiedInterpreter"
944 "Extend EditorWindow.close()"
946 response
= tkMessageBox
.askokcancel(
948 "The program is still running!\n Do you want to kill it?",
951 if response
is False:
957 # Wait for poll_subprocess() rescheduling to stop
958 self
.text
.after(2 * self
.pollinterval
, self
.close2
)
961 return EditorWindow
.close(self
)
964 "Extend EditorWindow._close(), shut down debugger and execution server"
965 self
.close_debugger()
967 self
.interp
.kill_subprocess()
968 # Restore std streams
969 sys
.stdout
= self
.save_stdout
970 sys
.stderr
= self
.save_stderr
971 sys
.stdin
= self
.save_stdin
975 self
.flist
.pyshell
= None
977 EditorWindow
._close
(self
)
979 def ispythonsource(self
, filename
):
980 "Override EditorWindow method: never remove the colorizer"
983 def short_title(self
):
984 return self
.shell_title
987 'Type "copyright", "credits" or "license()" for more information.'
993 client
= self
.interp
.start_subprocess()
998 nosub
= "==== No Subprocess ===="
999 self
.write("Python %s on %s\n%s\n%s" %
1000 (sys
.version
, sys
.platform
, self
.COPYRIGHT
, nosub
))
1003 Tkinter
._default
_root
= None # 03Jan04 KBK What's this?
1010 self
.top
.mainloop() # nested mainloop()
1013 line
= self
.text
.get("iomark", "end-1c")
1014 if len(line
) == 0: # may be EOF if we quit our mainloop with Ctrl-C
1016 if isinstance(line
, unicode):
1019 line
= line
.encode(IOBinding
.encoding
)
1020 except UnicodeError:
1025 if not use_subprocess
:
1026 raise KeyboardInterrupt
1035 def cancel_callback(self
, event
=None):
1037 if self
.text
.compare("sel.first", "!=", "sel.last"):
1038 return # Active selection -- always use default binding
1041 if not (self
.executing
or self
.reading
):
1043 self
.interp
.write("KeyboardInterrupt\n")
1048 if (self
.executing
and self
.interp
.rpcclt
):
1049 if self
.interp
.getdebugger():
1050 self
.interp
.restart_subprocess()
1052 self
.interp
.interrupt_subprocess()
1054 self
.top
.quit() # exit the nested mainloop() in readline()
1057 def eof_callback(self
, event
):
1058 if self
.executing
and not self
.reading
:
1059 return # Let the default binding (delete next char) take over
1060 if not (self
.text
.compare("iomark", "==", "insert") and
1061 self
.text
.compare("insert", "==", "end-1c")):
1062 return # Let the default binding (delete next char) take over
1063 if not self
.executing
:
1072 def linefeed_callback(self
, event
):
1073 # Insert a linefeed without entering anything (still autoindented)
1075 self
.text
.insert("insert", "\n")
1076 self
.text
.see("insert")
1078 self
.newline_and_indent_event(event
)
1081 def enter_callback(self
, event
):
1082 if self
.executing
and not self
.reading
:
1083 return # Let the default binding (insert '\n') take over
1084 # If some text is selected, recall the selection
1085 # (but only if this before the I/O mark)
1087 sel
= self
.text
.get("sel.first", "sel.last")
1089 if self
.text
.compare("sel.last", "<=", "iomark"):
1090 self
.recall(sel
, event
)
1094 # If we're strictly before the line containing iomark, recall
1095 # the current line, less a leading prompt, less leading or
1096 # trailing whitespace
1097 if self
.text
.compare("insert", "<", "iomark linestart"):
1098 # Check if there's a relevant stdin range -- if so, use it
1099 prev
= self
.text
.tag_prevrange("stdin", "insert")
1100 if prev
and self
.text
.compare("insert", "<", prev
[1]):
1101 self
.recall(self
.text
.get(prev
[0], prev
[1]), event
)
1103 next
= self
.text
.tag_nextrange("stdin", "insert")
1104 if next
and self
.text
.compare("insert lineend", ">=", next
[0]):
1105 self
.recall(self
.text
.get(next
[0], next
[1]), event
)
1107 # No stdin mark -- just get the current line, less any prompt
1108 indices
= self
.text
.tag_nextrange("console", "insert linestart")
1110 self
.text
.compare(indices
[0], "<=", "insert linestart"):
1111 self
.recall(self
.text
.get(indices
[1], "insert lineend"), event
)
1113 self
.recall(self
.text
.get("insert linestart", "insert lineend"), event
)
1115 # If we're between the beginning of the line and the iomark, i.e.
1116 # in the prompt area, move to the end of the prompt
1117 if self
.text
.compare("insert", "<", "iomark"):
1118 self
.text
.mark_set("insert", "iomark")
1119 # If we're in the current input and there's only whitespace
1120 # beyond the cursor, erase that whitespace first
1121 s
= self
.text
.get("insert", "end-1c")
1122 if s
and not s
.strip():
1123 self
.text
.delete("insert", "end-1c")
1124 # If we're in the current input before its last line,
1125 # insert a newline right at the insert point
1126 if self
.text
.compare("insert", "<", "end-1c linestart"):
1127 self
.newline_and_indent_event(event
)
1129 # We're in the last line; append a newline and submit it
1130 self
.text
.mark_set("insert", "end-1c")
1132 self
.text
.insert("insert", "\n")
1133 self
.text
.see("insert")
1135 self
.newline_and_indent_event(event
)
1136 self
.text
.tag_add("stdin", "iomark", "end-1c")
1137 self
.text
.update_idletasks()
1139 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
1144 def recall(self
, s
, event
):
1145 # remove leading and trailing empty or whitespace lines
1146 s
= re
.sub(r
'^\s*\n', '' , s
)
1147 s
= re
.sub(r
'\n\s*$', '', s
)
1148 lines
= s
.split('\n')
1149 self
.text
.undo_block_start()
1151 self
.text
.tag_remove("sel", "1.0", "end")
1152 self
.text
.mark_set("insert", "end-1c")
1153 prefix
= self
.text
.get("insert linestart", "insert")
1154 if prefix
.rstrip().endswith(':'):
1155 self
.newline_and_indent_event(event
)
1156 prefix
= self
.text
.get("insert linestart", "insert")
1157 self
.text
.insert("insert", lines
[0].strip())
1159 orig_base_indent
= re
.search(r
'^([ \t]*)', lines
[0]).group(0)
1160 new_base_indent
= re
.search(r
'^([ \t]*)', prefix
).group(0)
1161 for line
in lines
[1:]:
1162 if line
.startswith(orig_base_indent
):
1163 # replace orig base indentation with new indentation
1164 line
= new_base_indent
+ line
[len(orig_base_indent
):]
1165 self
.text
.insert('insert', '\n'+line
.rstrip())
1167 self
.text
.see("insert")
1168 self
.text
.undo_block_stop()
1171 line
= self
.text
.get("iomark", "end-1c")
1172 # Strip off last newline and surrounding whitespace.
1173 # (To allow you to hit return twice to end a statement.)
1175 while i
> 0 and line
[i
-1] in " \t":
1177 if i
> 0 and line
[i
-1] == "\n":
1179 while i
> 0 and line
[i
-1] in " \t":
1182 more
= self
.interp
.runsource(line
)
1184 def open_stack_viewer(self
, event
=None):
1185 if self
.interp
.rpcclt
:
1186 return self
.interp
.remote_stack_viewer()
1190 tkMessageBox
.showerror("No stack trace",
1191 "There is no stack trace yet.\n"
1192 "(sys.last_traceback is not defined)",
1195 from StackViewer
import StackBrowser
1196 sv
= StackBrowser(self
.root
, self
.flist
)
1198 def view_restart_mark(self
, event
=None):
1199 self
.text
.see("iomark")
1200 self
.text
.see("restart")
1202 def restart_shell(self
, event
=None):
1203 self
.interp
.restart_subprocess()
1205 def showprompt(self
):
1211 self
.console
.write(s
)
1212 self
.text
.mark_set("insert", "end-1c")
1213 self
.set_line_and_column()
1214 self
.io
.reset_undo()
1216 def resetoutput(self
):
1217 source
= self
.text
.get("iomark", "end-1c")
1219 self
.history
.history_store(source
)
1220 if self
.text
.get("end-2c") != "\n":
1221 self
.text
.insert("end-1c", "\n")
1222 self
.text
.mark_set("iomark", "end-1c")
1223 self
.set_line_and_column()
1224 sys
.stdout
.softspace
= 0
1226 def write(self
, s
, tags
=()):
1228 self
.text
.mark_gravity("iomark", "right")
1229 OutputWindow
.write(self
, s
, tags
, "iomark")
1230 self
.text
.mark_gravity("iomark", "left")
1235 if not use_subprocess
:
1236 raise KeyboardInterrupt
1238 class PseudoFile(object):
1240 def __init__(self
, shell
, tags
, encoding
=None):
1244 self
.encoding
= encoding
1247 self
.shell
.write(s
, self
.tags
)
1249 def writelines(self
, l
):
1261 USAGE: idle [-deins] [-t title] [file]*
1262 idle [-dns] [-t title] (-c cmd | -r file) [arg]*
1263 idle [-dns] [-t title] - [arg]*
1265 -h print this help message and exit
1266 -n run IDLE without a subprocess (see Help/IDLE Help for details)
1268 The following options will override the IDLE 'settings' configuration:
1270 -e open an edit window
1271 -i open a shell window
1273 The following options imply -i and will open a shell:
1275 -c cmd run the command in a shell, or
1276 -r file run script from file
1278 -d enable the debugger
1279 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1280 -t title set title of shell window
1282 A default edit window will be bypassed when -c, -r, or - are used.
1284 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1289 Open an edit window or shell depending on IDLE's configuration.
1291 idle foo.py foobar.py
1292 Edit the files, also open a shell if configured to start with shell.
1294 idle -est "Baz" foo.py
1295 Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1296 window with the title "Baz".
1298 idle -c "import sys; print sys.argv" "foo"
1299 Open a shell window and run the command, passing "-c" in sys.argv[0]
1300 and "foo" in sys.argv[1].
1302 idle -d -s -r foo.py "Hello World"
1303 Open a shell window, run a startup script, enable the debugger, and
1304 run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1307 echo "import sys; print sys.argv" | idle - "foobar"
1308 Open a shell window, run the script piped in, passing '' in sys.argv[0]
1309 and "foobar" in sys.argv[1].
1313 global flist
, root
, use_subprocess
1315 use_subprocess
= True
1323 opts
, args
= getopt
.getopt(sys
.argv
[1:], "c:deihnr:st:")
1324 except getopt
.error
, msg
:
1325 sys
.stderr
.write("Error: %s\n" % str(msg
))
1326 sys
.stderr
.write(usage_msg
)
1337 enable_shell
= False
1339 sys
.stdout
.write(usage_msg
)
1344 use_subprocess
= False
1347 if os
.path
.isfile(script
):
1350 print "No script file: ", script
1357 PyShell
.shell_title
= a
1359 if args
and args
[0] == '-':
1360 cmd
= sys
.stdin
.read()
1362 # process sys.argv and sys.path:
1363 for i
in range(len(sys
.path
)):
1364 sys
.path
[i
] = os
.path
.abspath(sys
.path
[i
])
1365 if args
and args
[0] == '-':
1366 sys
.argv
= [''] + args
[1:]
1368 sys
.argv
= ['-c'] + args
1370 sys
.argv
= [script
] + args
1374 for filename
in args
:
1375 pathx
.append(os
.path
.dirname(filename
))
1377 dir = os
.path
.abspath(dir)
1378 if not dir in sys
.path
:
1379 sys
.path
.insert(0, dir)
1382 if not dir in sys
.path
:
1383 sys
.path
.insert(0, dir)
1384 # check the IDLE settings configuration (but command line overrides)
1385 edit_start
= idleConf
.GetOption('main', 'General',
1386 'editor-on-startup', type='bool')
1387 enable_edit
= enable_edit
or edit_start
1388 # start editor and/or shell windows:
1389 root
= Tk(className
="Idle")
1393 flist
= PyShellFileList(root
)
1394 macosxSupport
.setupApp(root
, flist
)
1397 if not (cmd
or script
):
1398 for filename
in args
:
1399 flist
.open(filename
)
1403 shell
= flist
.open_shell()
1405 return # couldn't open shell
1407 if macosxSupport
.runningAsOSXApp() and flist
.dict:
1408 # On OSX: when the user has double-clicked on a file that causes
1409 # IDLE to be launched the shell window will open just in front of
1410 # the file she wants to see. Lower the interpreter window when
1411 # there are open files.
1414 shell
= flist
.pyshell
1415 # handle remaining options:
1417 shell
.open_debugger()
1419 filename
= os
.environ
.get("IDLESTARTUP") or \
1420 os
.environ
.get("PYTHONSTARTUP")
1421 if filename
and os
.path
.isfile(filename
):
1422 shell
.interp
.execfile(filename
)
1423 if shell
and cmd
or script
:
1424 shell
.interp
.runcommand("""if 1:
1428 \n""" % (sys
.argv
,))
1430 shell
.interp
.execsource(cmd
)
1432 shell
.interp
.prepend_syspath(script
)
1433 shell
.interp
.execfile(script
)
1438 if __name__
== "__main__":
1439 sys
.modules
['PyShell'] = sys
.modules
['__main__']