11 from code
import InteractiveInterpreter
16 from EditorWindow
import EditorWindow
, fixwordbreaks
17 from FileList
import FileList
18 from ColorDelegator
import ColorDelegator
19 from UndoDelegator
import UndoDelegator
20 from OutputWindow
import OutputWindow
21 from IdleConf
import idleconf
24 # We need to patch linecache.checkcache, because we don't want it
25 # to throw away our <pyshell#...> entries.
26 # Rather than repeating its code here, we save those entries,
27 # then call the original function, and then restore the saved entries.
28 def linecache_checkcache(orig_checkcache
=linecache
.checkcache
):
29 cache
= linecache
.cache
31 for filename
in cache
.keys():
32 if filename
[:1] + filename
[-1:] == '<>':
33 save
[filename
] = cache
[filename
]
36 linecache
.checkcache
= linecache_checkcache
39 IDENTCHARS
= string
.ascii_letters
+ string
.digits
+ "_"
42 # Note: <<newline-and-indent>> event is defined in AutoIndent.py
44 #$ event <<plain-newline-and-indent>>
48 #$ event <<beginning-of-line>>
54 #$ event <<history-next>>
58 #$ event <<history-previous>>
62 #$ event <<interrupt-execution>>
66 #$ event <<end-of-file>>
70 #$ event <<open-stack-viewer>>
72 #$ event <<toggle-debugger>>
75 class PyShellEditorWindow(EditorWindow
):
77 # Regular text edit window when a shell is present
78 # XXX ought to merge with regular editor window
79 runnable
= True # Shell not present, enable Import Module and Run Script
81 def __init__(self
, *args
):
82 apply(EditorWindow
.__init
__, (self
,) + args
)
83 self
.text
.bind("<<set-breakpoint-here>>", self
.set_breakpoint_here
)
84 self
.text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
87 ("Set breakpoint here", "<<set-breakpoint-here>>"),
90 def set_breakpoint_here(self
, event
=None):
91 if not self
.flist
.pyshell
or not self
.flist
.pyshell
.interp
.debugger
:
94 self
.flist
.pyshell
.interp
.debugger
.set_breakpoint_here(self
)
97 class PyShellFileList(FileList
):
99 # File list when a shell is present
101 EditorWindow
= PyShellEditorWindow
105 def open_shell(self
, event
=None):
107 self
.pyshell
.wakeup()
109 self
.pyshell
= PyShell(self
)
114 class ModifiedColorDelegator(ColorDelegator
):
116 # Colorizer for the shell window itself
118 def recolorize_main(self
):
119 self
.tag_remove("TODO", "1.0", "iomark")
120 self
.tag_add("SYNC", "1.0", "iomark")
121 ColorDelegator
.recolorize_main(self
)
123 tagdefs
= ColorDelegator
.tagdefs
.copy()
124 cconf
= idleconf
.getsection('Colors')
127 "stdin": cconf
.getcolor("stdin"),
128 "stdout": cconf
.getcolor("stdout"),
129 "stderr": cconf
.getcolor("stderr"),
130 "console": cconf
.getcolor("console"),
131 "ERROR": cconf
.getcolor("ERROR"),
132 None: cconf
.getcolor("normal"),
136 class ModifiedUndoDelegator(UndoDelegator
):
138 # Forbid insert/delete before the I/O mark
140 def insert(self
, index
, chars
, tags
=None):
142 if self
.delegate
.compare(index
, "<", "iomark"):
147 UndoDelegator
.insert(self
, index
, chars
, tags
)
149 def delete(self
, index1
, index2
=None):
151 if self
.delegate
.compare(index1
, "<", "iomark"):
156 UndoDelegator
.delete(self
, index1
, index2
)
158 class ModifiedInterpreter(InteractiveInterpreter
):
160 def __init__(self
, tkconsole
):
161 self
.tkconsole
= tkconsole
162 locals = sys
.modules
['__main__'].__dict
__
163 InteractiveInterpreter
.__init
__(self
, locals=locals)
164 self
.save_warnings_filters
= None
168 def execsource(self
, source
):
169 # Like runsource() but assumes complete exec source
170 filename
= self
.stuffsource(source
)
171 self
.execfile(filename
, source
)
173 def execfile(self
, filename
, source
=None):
174 # Execute an existing file
176 source
= open(filename
, "r").read()
178 code
= compile(source
, filename
, "exec")
179 except (OverflowError, SyntaxError):
180 self
.tkconsole
.resetoutput()
181 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
185 def runsource(self
, source
):
186 # Extend base class to stuff the source in the line cache first
187 filename
= self
.stuffsource(source
)
189 self
.save_warnings_filters
= warnings
.filters
[:]
190 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
192 return InteractiveInterpreter
.runsource(self
, source
, filename
)
194 if self
.save_warnings_filters
is not None:
195 warnings
.filters
[:] = self
.save_warnings_filters
196 self
.save_warnings_filters
= None
198 def stuffsource(self
, source
):
199 # Stuff source in the filename cache
200 filename
= "<pyshell#%d>" % self
.gid
201 self
.gid
= self
.gid
+ 1
202 lines
= string
.split(source
, "\n")
203 linecache
.cache
[filename
] = len(source
)+1, 0, lines
, filename
206 def showsyntaxerror(self
, filename
=None):
207 # Extend base class to color the offending position
208 # (instead of printing it and pointing at it with a caret)
209 text
= self
.tkconsole
.text
210 stuff
= self
.unpackerror()
212 self
.tkconsole
.resetoutput()
213 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
215 msg
, lineno
, offset
, line
= stuff
217 pos
= "iomark + %d chars" % (offset
-1)
219 pos
= "iomark linestart + %d lines + %d chars" % (lineno
-1,
221 text
.tag_add("ERROR", pos
)
224 if char
and char
in IDENTCHARS
:
225 text
.tag_add("ERROR", pos
+ " wordstart", pos
)
226 self
.tkconsole
.resetoutput()
227 self
.write("SyntaxError: %s\n" % str(msg
))
229 def unpackerror(self
):
230 type, value
, tb
= sys
.exc_info()
231 ok
= type is SyntaxError
234 msg
, (dummy_filename
, lineno
, offset
, line
) = value
238 return msg
, lineno
, offset
, line
242 def showtraceback(self
):
243 # Extend base class method to reset output properly
244 text
= self
.tkconsole
.text
245 self
.tkconsole
.resetoutput()
246 self
.checklinecache()
247 InteractiveInterpreter
.showtraceback(self
)
249 def checklinecache(self
):
252 if key
[:1] + key
[-1:] != "<>":
257 def setdebugger(self
, debugger
):
258 self
.debugger
= debugger
260 def getdebugger(self
):
263 def runcode(self
, code
):
264 # Override base class method
265 if self
.save_warnings_filters
is not None:
266 warnings
.filters
[:] = self
.save_warnings_filters
267 self
.save_warnings_filters
= None
268 debugger
= self
.debugger
270 self
.tkconsole
.beginexecuting()
273 debugger
.run(code
, self
.locals)
275 exec code
in self
.locals
277 if tkMessageBox
.askyesno(
279 "Do you want to exit altogether?",
281 master
=self
.tkconsole
.text
):
285 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
286 self
.tkconsole
.open_stack_viewer()
289 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
290 self
.tkconsole
.open_stack_viewer()
293 self
.tkconsole
.endexecuting()
296 # Override base class write
297 self
.tkconsole
.console
.write(s
)
300 class PyShell(OutputWindow
):
302 shell_title
= "Python Shell"
305 ColorDelegator
= ModifiedColorDelegator
306 UndoDelegator
= ModifiedUndoDelegator
308 # Override menu bar specs
309 menu_specs
= PyShellEditorWindow
.menu_specs
[:]
310 menu_specs
.insert(len(menu_specs
)-2, ("debug", "_Debug"))
313 from IdleHistory
import History
315 def __init__(self
, flist
=None):
316 self
.interp
= ModifiedInterpreter(self
)
321 flist
= PyShellFileList(root
)
323 OutputWindow
.__init
__(self
, flist
, None, None)
326 __builtin__
.quit
= __builtin__
.exit
= "To exit, type Ctrl-D."
328 self
.auto
= self
.extensions
["AutoIndent"] # Required extension
329 self
.auto
.config(usetabs
=1, indentwidth
=8, context_use_ps1
=1)
332 text
.configure(wrap
="char")
333 text
.bind("<<newline-and-indent>>", self
.enter_callback
)
334 text
.bind("<<plain-newline-and-indent>>", self
.linefeed_callback
)
335 text
.bind("<<interrupt-execution>>", self
.cancel_callback
)
336 text
.bind("<<beginning-of-line>>", self
.home_callback
)
337 text
.bind("<<end-of-file>>", self
.eof_callback
)
338 text
.bind("<<open-stack-viewer>>", self
.open_stack_viewer
)
339 text
.bind("<<toggle-debugger>>", self
.toggle_debugger
)
340 text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
341 text
.bind("<<toggle-jit-stack-viewer>>", self
.toggle_jit_stack_viewer
)
343 self
.save_stdout
= sys
.stdout
344 self
.save_stderr
= sys
.stderr
345 self
.save_stdin
= sys
.stdin
346 sys
.stdout
= PseudoFile(self
, "stdout")
347 sys
.stderr
= PseudoFile(self
, "stderr")
349 self
.console
= PseudoFile(self
, "console")
351 self
.history
= self
.History(self
.text
)
358 def toggle_debugger(self
, event
=None):
360 tkMessageBox
.showerror("Don't debug now",
361 "You can only toggle the debugger when idle",
363 self
.set_debugger_indicator()
366 db
= self
.interp
.getdebugger()
368 self
.close_debugger()
372 def set_debugger_indicator(self
):
373 db
= self
.interp
.getdebugger()
374 self
.setvar("<<toggle-debugger>>", not not db
)
376 def toggle_jit_stack_viewer( self
, event
=None):
377 pass # All we need is the variable
379 def close_debugger(self
):
380 db
= self
.interp
.getdebugger()
382 self
.interp
.setdebugger(None)
385 self
.console
.write("[DEBUG OFF]\n")
388 self
.set_debugger_indicator()
390 def open_debugger(self
):
392 self
.interp
.setdebugger(Debugger
.Debugger(self
))
393 sys
.ps1
= "[DEBUG ON]\n>>> "
395 self
.set_debugger_indicator()
397 def beginexecuting(self
):
398 # Helper for ModifiedInterpreter
401 ##self._cancel_check = self.cancel_check
402 ##sys.settrace(self._cancel_check)
404 def endexecuting(self
):
405 # Helper for ModifiedInterpreter
407 ##self._cancel_check = None
412 # Extend base class method
414 # XXX Need to ask a question here
415 if not tkMessageBox
.askokcancel(
417 "The program is still running; do you want to kill it?",
425 return OutputWindow
.close(self
)
428 self
.close_debugger()
429 # Restore std streams
430 sys
.stdout
= self
.save_stdout
431 sys
.stderr
= self
.save_stderr
432 sys
.stdin
= self
.save_stdin
437 self
.flist
.pyshell
= None
439 OutputWindow
._close
(self
) # Really EditorWindow._close
441 def ispythonsource(self
, filename
):
442 # Override this so EditorWindow never removes the colorizer
445 def short_title(self
):
446 return self
.shell_title
449 'Type "copyright", "credits" or "license" for more information.'
453 self
.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
454 (sys
.version
, sys
.platform
, self
.COPYRIGHT
,
455 idlever
.IDLE_VERSION
))
458 except AttributeError:
462 Tkinter
._default
_root
= None
475 line
= self
.text
.get("iomark", "end-1c")
479 raise KeyboardInterrupt
488 def cancel_callback(self
, event
):
490 if self
.text
.compare("sel.first", "!=", "sel.last"):
491 return # Active selection -- always use default binding
494 if not (self
.executing
or self
.reading
):
496 self
.write("KeyboardInterrupt\n")
505 def eof_callback(self
, event
):
506 if self
.executing
and not self
.reading
:
507 return # Let the default binding (delete next char) take over
508 if not (self
.text
.compare("iomark", "==", "insert") and
509 self
.text
.compare("insert", "==", "end-1c")):
510 return # Let the default binding (delete next char) take over
511 if not self
.executing
:
512 ## if not tkMessageBox.askokcancel(
514 ## "Are you sure you want to exit?",
515 ## default="ok", master=self.text):
525 def home_callback(self
, event
):
526 if event
.state
!= 0 and event
.keysym
== "Home":
527 return # <Modifier-Home>; fall back to class binding
528 if self
.text
.compare("iomark", "<=", "insert") and \
529 self
.text
.compare("insert linestart", "<=", "iomark"):
530 self
.text
.mark_set("insert", "iomark")
531 self
.text
.tag_remove("sel", "1.0", "end")
532 self
.text
.see("insert")
535 def linefeed_callback(self
, event
):
536 # Insert a linefeed without entering anything (still autoindented)
538 self
.text
.insert("insert", "\n")
539 self
.text
.see("insert")
541 self
.auto
.auto_indent(event
)
544 def enter_callback(self
, event
):
545 if self
.executing
and not self
.reading
:
546 return # Let the default binding (insert '\n') take over
547 # If some text is selected, recall the selection
548 # (but only if this before the I/O mark)
550 sel
= self
.text
.get("sel.first", "sel.last")
552 if self
.text
.compare("sel.last", "<=", "iomark"):
557 # If we're strictly before the line containing iomark, recall
558 # the current line, less a leading prompt, less leading or
559 # trailing whitespace
560 if self
.text
.compare("insert", "<", "iomark linestart"):
561 # Check if there's a relevant stdin range -- if so, use it
562 prev
= self
.text
.tag_prevrange("stdin", "insert")
563 if prev
and self
.text
.compare("insert", "<", prev
[1]):
564 self
.recall(self
.text
.get(prev
[0], prev
[1]))
566 next
= self
.text
.tag_nextrange("stdin", "insert")
567 if next
and self
.text
.compare("insert lineend", ">=", next
[0]):
568 self
.recall(self
.text
.get(next
[0], next
[1]))
570 # No stdin mark -- just get the current line
571 self
.recall(self
.text
.get("insert linestart", "insert lineend"))
573 # If we're in the current input and there's only whitespace
574 # beyond the cursor, erase that whitespace first
575 s
= self
.text
.get("insert", "end-1c")
576 if s
and not string
.strip(s
):
577 self
.text
.delete("insert", "end-1c")
578 # If we're in the current input before its last line,
579 # insert a newline right at the insert point
580 if self
.text
.compare("insert", "<", "end-1c linestart"):
581 self
.auto
.auto_indent(event
)
583 # We're in the last line; append a newline and submit it
584 self
.text
.mark_set("insert", "end-1c")
586 self
.text
.insert("insert", "\n")
587 self
.text
.see("insert")
589 self
.auto
.auto_indent(event
)
590 self
.text
.tag_add("stdin", "iomark", "end-1c")
591 self
.text
.update_idletasks()
593 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
600 self
.history
.recall(s
)
603 line
= self
.text
.get("iomark", "end-1c")
604 # Strip off last newline and surrounding whitespace.
605 # (To allow you to hit return twice to end a statement.)
607 while i
> 0 and line
[i
-1] in " \t":
609 if i
> 0 and line
[i
-1] == "\n":
611 while i
> 0 and line
[i
-1] in " \t":
614 more
= self
.interp
.runsource(line
)
618 def cancel_check(self
, frame
, what
, args
,
619 dooneevent
=tkinter
.dooneevent
,
620 dontwait
=tkinter
.DONT_WAIT
):
621 # Hack -- use the debugger hooks to be able to handle events
622 # and interrupt execution at any time.
623 # This slows execution down quite a bit, so you may want to
624 # disable this (by not calling settrace() in runcode() above)
625 # for full-bore (uninterruptable) speed.
626 # XXX This should become a user option.
632 raise KeyboardInterrupt
633 return self
._cancel
_check
635 def open_stack_viewer(self
, event
=None):
639 tkMessageBox
.showerror("No stack trace",
640 "There is no stack trace yet.\n"
641 "(sys.last_traceback is not defined)",
644 from StackViewer
import StackBrowser
645 sv
= StackBrowser(self
.root
, self
.flist
)
647 def showprompt(self
):
653 self
.console
.write(s
)
654 self
.text
.mark_set("insert", "end-1c")
656 def resetoutput(self
):
657 source
= self
.text
.get("iomark", "end-1c")
659 self
.history
.history_store(source
)
660 if self
.text
.get("end-2c") != "\n":
661 self
.text
.insert("end-1c", "\n")
662 self
.text
.mark_set("iomark", "end-1c")
663 sys
.stdout
.softspace
= 0
665 def write(self
, s
, tags
=()):
666 self
.text
.mark_gravity("iomark", "right")
667 OutputWindow
.write(self
, s
, tags
, "iomark")
668 self
.text
.mark_gravity("iomark", "left")
671 raise KeyboardInterrupt
675 def __init__(self
, shell
, tags
):
680 self
.shell
.write(s
, self
.tags
)
682 def writelines(self
, l
):
693 usage: idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
695 -c command run this command
697 -e edit mode; arguments are files to be edited
698 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
699 -t title set title of shell window
701 When neither -c nor -e is used, and there are arguments, and the first
702 argument is not '-', the first argument is run as a script. Remaining
703 arguments are arguments to the script or to the command run by -c.
713 opts
, args
= getopt
.getopt(sys
.argv
[1:], "c:deist:")
714 except getopt
.error
, msg
:
715 sys
.stderr
.write("Error: %s\n" % str(msg
))
716 sys
.stderr
.write(usage_msg
)
729 PyShell
.shell_title
= a
731 for i
in range(len(sys
.path
)):
732 sys
.path
[i
] = os
.path
.abspath(sys
.path
[i
])
736 for filename
in args
:
737 pathx
.append(os
.path
.dirname(filename
))
738 elif args
and args
[0] != "-":
739 pathx
.append(os
.path
.dirname(args
[0]))
741 pathx
.append(os
.curdir
)
743 dir = os
.path
.abspath(dir)
744 if not dir in sys
.path
:
745 sys
.path
.insert(0, dir)
748 root
= Tk(className
="Idle")
751 flist
= PyShellFileList(root
)
754 for filename
in args
:
758 sys
.argv
= ["-c"] + args
760 sys
.argv
= args
or [""]
763 shell
= PyShell(flist
)
764 interp
= shell
.interp
765 flist
.pyshell
= shell
768 filename
= os
.environ
.get("IDLESTARTUP") or \
769 os
.environ
.get("PYTHONSTARTUP")
770 if filename
and os
.path
.isfile(filename
):
771 interp
.execfile(filename
)
774 shell
.open_debugger()
776 interp
.execsource(cmd
)
777 elif not edit
and args
and args
[0] != "-":
778 interp
.execfile(args
[0])
785 if __name__
== "__main__":