12 from code
import InteractiveInterpreter
17 from EditorWindow
import EditorWindow
, fixwordbreaks
18 from FileList
import FileList
19 from ColorDelegator
import ColorDelegator
20 from UndoDelegator
import UndoDelegator
21 from OutputWindow
import OutputWindow
22 from IdleConf
import idleconf
25 # We need to patch linecache.checkcache, because we don't want it
26 # to throw away our <pyshell#...> entries.
27 # Rather than repeating its code here, we save those entries,
28 # then call the original function, and then restore the saved entries.
29 def linecache_checkcache(orig_checkcache
=linecache
.checkcache
):
30 cache
= linecache
.cache
32 for filename
in cache
.keys():
33 if filename
[:1] + filename
[-1:] == '<>':
34 save
[filename
] = cache
[filename
]
37 linecache
.checkcache
= linecache_checkcache
40 IDENTCHARS
= string
.ascii_letters
+ string
.digits
+ "_"
43 # Note: <<newline-and-indent>> event is defined in AutoIndent.py
45 #$ event <<plain-newline-and-indent>>
49 #$ event <<beginning-of-line>>
55 #$ event <<history-next>>
59 #$ event <<history-previous>>
63 #$ event <<interrupt-execution>>
67 #$ event <<end-of-file>>
71 #$ event <<open-stack-viewer>>
73 #$ event <<toggle-debugger>>
76 class PyShellEditorWindow(EditorWindow
):
78 # Regular text edit window when a shell is present
79 # XXX ought to merge with regular editor window
80 runnable
= True # Shell not present, enable Import Module and Run Script
82 def __init__(self
, *args
):
83 apply(EditorWindow
.__init
__, (self
,) + args
)
84 self
.text
.bind("<<set-breakpoint-here>>", self
.set_breakpoint_here
)
85 self
.text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
88 ("Set breakpoint here", "<<set-breakpoint-here>>"),
91 def set_breakpoint_here(self
, event
=None):
92 if not self
.flist
.pyshell
or not self
.flist
.pyshell
.interp
.debugger
:
95 self
.flist
.pyshell
.interp
.debugger
.set_breakpoint_here(self
)
98 class PyShellFileList(FileList
):
100 # File list when a shell is present
102 EditorWindow
= PyShellEditorWindow
106 def open_shell(self
, event
=None):
108 self
.pyshell
.wakeup()
110 self
.pyshell
= PyShell(self
)
115 class ModifiedColorDelegator(ColorDelegator
):
117 # Colorizer for the shell window itself
119 def recolorize_main(self
):
120 self
.tag_remove("TODO", "1.0", "iomark")
121 self
.tag_add("SYNC", "1.0", "iomark")
122 ColorDelegator
.recolorize_main(self
)
124 tagdefs
= ColorDelegator
.tagdefs
.copy()
125 cconf
= idleconf
.getsection('Colors')
128 "stdin": cconf
.getcolor("stdin"),
129 "stdout": cconf
.getcolor("stdout"),
130 "stderr": cconf
.getcolor("stderr"),
131 "console": cconf
.getcolor("console"),
132 "ERROR": cconf
.getcolor("ERROR"),
133 None: cconf
.getcolor("normal"),
137 class ModifiedUndoDelegator(UndoDelegator
):
139 # Forbid insert/delete before the I/O mark
141 def insert(self
, index
, chars
, tags
=None):
143 if self
.delegate
.compare(index
, "<", "iomark"):
148 UndoDelegator
.insert(self
, index
, chars
, tags
)
150 def delete(self
, index1
, index2
=None):
152 if self
.delegate
.compare(index1
, "<", "iomark"):
157 UndoDelegator
.delete(self
, index1
, index2
)
159 class ModifiedInterpreter(InteractiveInterpreter
):
161 def __init__(self
, tkconsole
):
162 self
.tkconsole
= tkconsole
163 locals = sys
.modules
['__main__'].__dict
__
164 InteractiveInterpreter
.__init
__(self
, locals=locals)
165 self
.save_warnings_filters
= None
169 def execsource(self
, source
):
170 # Like runsource() but assumes complete exec source
171 filename
= self
.stuffsource(source
)
172 self
.execfile(filename
, source
)
174 def execfile(self
, filename
, source
=None):
175 # Execute an existing file
177 source
= open(filename
, "r").read()
179 code
= compile(source
, filename
, "exec")
180 except (OverflowError, SyntaxError):
181 self
.tkconsole
.resetoutput()
182 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
186 def runsource(self
, source
):
187 # Extend base class to stuff the source in the line cache first
188 filename
= self
.stuffsource(source
)
190 self
.save_warnings_filters
= warnings
.filters
[:]
191 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
192 if isinstance(source
, types
.UnicodeType
):
195 source
= source
.encode(IOBinding
.encoding
)
197 self
.tkconsole
.resetoutput()
198 self
.write("Unsupported characters in input")
201 return InteractiveInterpreter
.runsource(self
, source
, filename
)
203 if self
.save_warnings_filters
is not None:
204 warnings
.filters
[:] = self
.save_warnings_filters
205 self
.save_warnings_filters
= None
207 def stuffsource(self
, source
):
208 # Stuff source in the filename cache
209 filename
= "<pyshell#%d>" % self
.gid
210 self
.gid
= self
.gid
+ 1
211 lines
= source
.split("\n")
212 linecache
.cache
[filename
] = len(source
)+1, 0, lines
, filename
215 def showsyntaxerror(self
, filename
=None):
216 # Extend base class to color the offending position
217 # (instead of printing it and pointing at it with a caret)
218 text
= self
.tkconsole
.text
219 stuff
= self
.unpackerror()
221 self
.tkconsole
.resetoutput()
222 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
224 msg
, lineno
, offset
, line
= stuff
226 pos
= "iomark + %d chars" % (offset
-1)
228 pos
= "iomark linestart + %d lines + %d chars" % (lineno
-1,
230 text
.tag_add("ERROR", pos
)
233 if char
and char
in IDENTCHARS
:
234 text
.tag_add("ERROR", pos
+ " wordstart", pos
)
235 self
.tkconsole
.resetoutput()
236 self
.write("SyntaxError: %s\n" % str(msg
))
238 def unpackerror(self
):
239 type, value
, tb
= sys
.exc_info()
240 ok
= type is SyntaxError
243 msg
, (dummy_filename
, lineno
, offset
, line
) = value
247 return msg
, lineno
, offset
, line
251 def showtraceback(self
):
252 # Extend base class method to reset output properly
253 self
.tkconsole
.resetoutput()
254 self
.checklinecache()
255 InteractiveInterpreter
.showtraceback(self
)
257 def checklinecache(self
):
260 if key
[:1] + key
[-1:] != "<>":
265 def setdebugger(self
, debugger
):
266 self
.debugger
= debugger
268 def getdebugger(self
):
271 def runcode(self
, code
):
272 # Override base class method
273 if self
.save_warnings_filters
is not None:
274 warnings
.filters
[:] = self
.save_warnings_filters
275 self
.save_warnings_filters
= None
276 debugger
= self
.debugger
278 self
.tkconsole
.beginexecuting()
281 debugger
.run(code
, self
.locals)
283 exec code
in self
.locals
285 if tkMessageBox
.askyesno(
287 "Do you want to exit altogether?",
289 master
=self
.tkconsole
.text
):
293 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
294 self
.tkconsole
.open_stack_viewer()
297 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
298 self
.tkconsole
.open_stack_viewer()
301 self
.tkconsole
.endexecuting()
304 # Override base class write
305 self
.tkconsole
.console
.write(s
)
308 class PyShell(OutputWindow
):
310 shell_title
= "Python Shell"
313 ColorDelegator
= ModifiedColorDelegator
314 UndoDelegator
= ModifiedUndoDelegator
316 # Override menu bar specs
317 menu_specs
= PyShellEditorWindow
.menu_specs
[:]
318 menu_specs
.insert(len(menu_specs
)-2, ("debug", "_Debug"))
321 from IdleHistory
import History
323 def __init__(self
, flist
=None):
324 self
.interp
= ModifiedInterpreter(self
)
329 flist
= PyShellFileList(root
)
331 OutputWindow
.__init
__(self
, flist
, None, None)
334 __builtin__
.quit
= __builtin__
.exit
= "To exit, type Ctrl-D."
336 self
.auto
= self
.extensions
["AutoIndent"] # Required extension
337 self
.auto
.config(usetabs
=1, indentwidth
=8, context_use_ps1
=1)
340 text
.configure(wrap
="char")
341 text
.bind("<<newline-and-indent>>", self
.enter_callback
)
342 text
.bind("<<plain-newline-and-indent>>", self
.linefeed_callback
)
343 text
.bind("<<interrupt-execution>>", self
.cancel_callback
)
344 text
.bind("<<beginning-of-line>>", self
.home_callback
)
345 text
.bind("<<end-of-file>>", self
.eof_callback
)
346 text
.bind("<<open-stack-viewer>>", self
.open_stack_viewer
)
347 text
.bind("<<toggle-debugger>>", self
.toggle_debugger
)
348 text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
349 text
.bind("<<toggle-jit-stack-viewer>>", self
.toggle_jit_stack_viewer
)
351 self
.save_stdout
= sys
.stdout
352 self
.save_stderr
= sys
.stderr
353 self
.save_stdin
= sys
.stdin
354 sys
.stdout
= PseudoFile(self
, "stdout")
355 sys
.stderr
= PseudoFile(self
, "stderr")
357 self
.console
= PseudoFile(self
, "console")
359 self
.history
= self
.History(self
.text
)
366 def toggle_debugger(self
, event
=None):
368 tkMessageBox
.showerror("Don't debug now",
369 "You can only toggle the debugger when idle",
371 self
.set_debugger_indicator()
374 db
= self
.interp
.getdebugger()
376 self
.close_debugger()
380 def set_debugger_indicator(self
):
381 db
= self
.interp
.getdebugger()
382 self
.setvar("<<toggle-debugger>>", not not db
)
384 def toggle_jit_stack_viewer( self
, event
=None):
385 pass # All we need is the variable
387 def close_debugger(self
):
388 db
= self
.interp
.getdebugger()
390 self
.interp
.setdebugger(None)
393 self
.console
.write("[DEBUG OFF]\n")
396 self
.set_debugger_indicator()
398 def open_debugger(self
):
400 self
.interp
.setdebugger(Debugger
.Debugger(self
))
401 sys
.ps1
= "[DEBUG ON]\n>>> "
403 self
.set_debugger_indicator()
405 def beginexecuting(self
):
406 # Helper for ModifiedInterpreter
409 ##self._cancel_check = self.cancel_check
410 ##sys.settrace(self._cancel_check)
412 def endexecuting(self
):
413 # Helper for ModifiedInterpreter
415 ##self._cancel_check = None
420 # Extend base class method
422 # XXX Need to ask a question here
423 if not tkMessageBox
.askokcancel(
425 "The program is still running; do you want to kill it?",
433 return OutputWindow
.close(self
)
436 self
.close_debugger()
437 # Restore std streams
438 sys
.stdout
= self
.save_stdout
439 sys
.stderr
= self
.save_stderr
440 sys
.stdin
= self
.save_stdin
445 self
.flist
.pyshell
= None
447 OutputWindow
._close
(self
) # Really EditorWindow._close
449 def ispythonsource(self
, filename
):
450 # Override this so EditorWindow never removes the colorizer
453 def short_title(self
):
454 return self
.shell_title
457 'Type "copyright", "credits" or "license" for more information.'
461 self
.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
462 (sys
.version
, sys
.platform
, self
.COPYRIGHT
,
463 idlever
.IDLE_VERSION
))
466 except AttributeError:
470 Tkinter
._default
_root
= None
483 line
= self
.text
.get("iomark", "end-1c")
487 raise KeyboardInterrupt
496 def cancel_callback(self
, event
):
498 if self
.text
.compare("sel.first", "!=", "sel.last"):
499 return # Active selection -- always use default binding
502 if not (self
.executing
or self
.reading
):
504 self
.write("KeyboardInterrupt\n")
513 def eof_callback(self
, event
):
514 if self
.executing
and not self
.reading
:
515 return # Let the default binding (delete next char) take over
516 if not (self
.text
.compare("iomark", "==", "insert") and
517 self
.text
.compare("insert", "==", "end-1c")):
518 return # Let the default binding (delete next char) take over
519 if not self
.executing
:
520 ## if not tkMessageBox.askokcancel(
522 ## "Are you sure you want to exit?",
523 ## default="ok", master=self.text):
533 def home_callback(self
, event
):
534 if event
.state
!= 0 and event
.keysym
== "Home":
535 return # <Modifier-Home>; fall back to class binding
536 if self
.text
.compare("iomark", "<=", "insert") and \
537 self
.text
.compare("insert linestart", "<=", "iomark"):
538 self
.text
.mark_set("insert", "iomark")
539 self
.text
.tag_remove("sel", "1.0", "end")
540 self
.text
.see("insert")
543 def linefeed_callback(self
, event
):
544 # Insert a linefeed without entering anything (still autoindented)
546 self
.text
.insert("insert", "\n")
547 self
.text
.see("insert")
549 self
.auto
.auto_indent(event
)
552 def enter_callback(self
, event
):
553 if self
.executing
and not self
.reading
:
554 return # Let the default binding (insert '\n') take over
555 # If some text is selected, recall the selection
556 # (but only if this before the I/O mark)
558 sel
= self
.text
.get("sel.first", "sel.last")
560 if self
.text
.compare("sel.last", "<=", "iomark"):
565 # If we're strictly before the line containing iomark, recall
566 # the current line, less a leading prompt, less leading or
567 # trailing whitespace
568 if self
.text
.compare("insert", "<", "iomark linestart"):
569 # Check if there's a relevant stdin range -- if so, use it
570 prev
= self
.text
.tag_prevrange("stdin", "insert")
571 if prev
and self
.text
.compare("insert", "<", prev
[1]):
572 self
.recall(self
.text
.get(prev
[0], prev
[1]))
574 next
= self
.text
.tag_nextrange("stdin", "insert")
575 if next
and self
.text
.compare("insert lineend", ">=", next
[0]):
576 self
.recall(self
.text
.get(next
[0], next
[1]))
578 # No stdin mark -- just get the current line
579 self
.recall(self
.text
.get("insert linestart", "insert lineend"))
581 # If we're in the current input and there's only whitespace
582 # beyond the cursor, erase that whitespace first
583 s
= self
.text
.get("insert", "end-1c")
584 if s
and not s
.strip():
585 self
.text
.delete("insert", "end-1c")
586 # If we're in the current input before its last line,
587 # insert a newline right at the insert point
588 if self
.text
.compare("insert", "<", "end-1c linestart"):
589 self
.auto
.auto_indent(event
)
591 # We're in the last line; append a newline and submit it
592 self
.text
.mark_set("insert", "end-1c")
594 self
.text
.insert("insert", "\n")
595 self
.text
.see("insert")
597 self
.auto
.auto_indent(event
)
598 self
.text
.tag_add("stdin", "iomark", "end-1c")
599 self
.text
.update_idletasks()
601 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
608 self
.history
.recall(s
)
611 line
= self
.text
.get("iomark", "end-1c")
612 # Strip off last newline and surrounding whitespace.
613 # (To allow you to hit return twice to end a statement.)
615 while i
> 0 and line
[i
-1] in " \t":
617 if i
> 0 and line
[i
-1] == "\n":
619 while i
> 0 and line
[i
-1] in " \t":
622 more
= self
.interp
.runsource(line
)
626 def cancel_check(self
, frame
, what
, args
,
627 dooneevent
=tkinter
.dooneevent
,
628 dontwait
=tkinter
.DONT_WAIT
):
629 # Hack -- use the debugger hooks to be able to handle events
630 # and interrupt execution at any time.
631 # This slows execution down quite a bit, so you may want to
632 # disable this (by not calling settrace() in runcode() above)
633 # for full-bore (uninterruptable) speed.
634 # XXX This should become a user option.
640 raise KeyboardInterrupt
641 return self
._cancel
_check
643 def open_stack_viewer(self
, event
=None):
647 tkMessageBox
.showerror("No stack trace",
648 "There is no stack trace yet.\n"
649 "(sys.last_traceback is not defined)",
652 from StackViewer
import StackBrowser
653 sv
= StackBrowser(self
.root
, self
.flist
)
655 def showprompt(self
):
661 self
.console
.write(s
)
662 self
.text
.mark_set("insert", "end-1c")
664 def resetoutput(self
):
665 source
= self
.text
.get("iomark", "end-1c")
667 self
.history
.history_store(source
)
668 if self
.text
.get("end-2c") != "\n":
669 self
.text
.insert("end-1c", "\n")
670 self
.text
.mark_set("iomark", "end-1c")
671 sys
.stdout
.softspace
= 0
673 def write(self
, s
, tags
=()):
674 self
.text
.mark_gravity("iomark", "right")
675 OutputWindow
.write(self
, s
, tags
, "iomark")
676 self
.text
.mark_gravity("iomark", "left")
679 raise KeyboardInterrupt
683 def __init__(self
, shell
, tags
):
688 self
.shell
.write(s
, self
.tags
)
690 def writelines(self
, l
):
701 usage: idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
703 -c command run this command
705 -e edit mode; arguments are files to be edited
706 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
707 -t title set title of shell window
709 When neither -c nor -e is used, and there are arguments, and the first
710 argument is not '-', the first argument is run as a script. Remaining
711 arguments are arguments to the script or to the command run by -c.
721 opts
, args
= getopt
.getopt(sys
.argv
[1:], "c:deist:")
722 except getopt
.error
, msg
:
723 sys
.stderr
.write("Error: %s\n" % str(msg
))
724 sys
.stderr
.write(usage_msg
)
737 PyShell
.shell_title
= a
739 for i
in range(len(sys
.path
)):
740 sys
.path
[i
] = os
.path
.abspath(sys
.path
[i
])
744 for filename
in args
:
745 pathx
.append(os
.path
.dirname(filename
))
746 elif args
and args
[0] != "-":
747 pathx
.append(os
.path
.dirname(args
[0]))
749 pathx
.append(os
.curdir
)
751 dir = os
.path
.abspath(dir)
752 if not dir in sys
.path
:
753 sys
.path
.insert(0, dir)
756 root
= Tk(className
="Idle")
759 flist
= PyShellFileList(root
)
762 for filename
in args
:
766 sys
.argv
= ["-c"] + args
768 sys
.argv
= args
or [""]
771 shell
= PyShell(flist
)
772 interp
= shell
.interp
773 flist
.pyshell
= shell
776 filename
= os
.environ
.get("IDLESTARTUP") or \
777 os
.environ
.get("PYTHONSTARTUP")
778 if filename
and os
.path
.isfile(filename
):
779 interp
.execfile(filename
)
782 shell
.open_debugger()
784 interp
.execsource(cmd
)
785 elif not edit
and args
and args
[0] != "-":
786 interp
.execfile(args
[0])
793 if __name__
== "__main__":