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
80 def __init__(self
, *args
):
81 apply(EditorWindow
.__init
__, (self
,) + args
)
82 self
.text
.bind("<<set-breakpoint-here>>", self
.set_breakpoint_here
)
83 self
.text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
86 ("Set breakpoint here", "<<set-breakpoint-here>>"),
89 def set_breakpoint_here(self
, event
=None):
90 if not self
.flist
.pyshell
or not self
.flist
.pyshell
.interp
.debugger
:
93 self
.flist
.pyshell
.interp
.debugger
.set_breakpoint_here(self
)
96 class PyShellFileList(FileList
):
98 # File list when a shell is present
100 EditorWindow
= PyShellEditorWindow
104 def open_shell(self
, event
=None):
106 self
.pyshell
.wakeup()
108 self
.pyshell
= PyShell(self
)
113 class ModifiedColorDelegator(ColorDelegator
):
115 # Colorizer for the shell window itself
117 def recolorize_main(self
):
118 self
.tag_remove("TODO", "1.0", "iomark")
119 self
.tag_add("SYNC", "1.0", "iomark")
120 ColorDelegator
.recolorize_main(self
)
122 tagdefs
= ColorDelegator
.tagdefs
.copy()
123 cconf
= idleconf
.getsection('Colors')
126 "stdin": cconf
.getcolor("stdin"),
127 "stdout": cconf
.getcolor("stdout"),
128 "stderr": cconf
.getcolor("stderr"),
129 "console": cconf
.getcolor("console"),
130 "ERROR": cconf
.getcolor("ERROR"),
131 None: cconf
.getcolor("normal"),
135 class ModifiedUndoDelegator(UndoDelegator
):
137 # Forbid insert/delete before the I/O mark
139 def insert(self
, index
, chars
, tags
=None):
141 if self
.delegate
.compare(index
, "<", "iomark"):
146 UndoDelegator
.insert(self
, index
, chars
, tags
)
148 def delete(self
, index1
, index2
=None):
150 if self
.delegate
.compare(index1
, "<", "iomark"):
155 UndoDelegator
.delete(self
, index1
, index2
)
157 class ModifiedInterpreter(InteractiveInterpreter
):
159 def __init__(self
, tkconsole
):
160 self
.tkconsole
= tkconsole
161 locals = sys
.modules
['__main__'].__dict
__
162 InteractiveInterpreter
.__init
__(self
, locals=locals)
163 self
.save_warnings_filters
= None
167 def execsource(self
, source
):
168 # Like runsource() but assumes complete exec source
169 filename
= self
.stuffsource(source
)
170 self
.execfile(filename
, source
)
172 def execfile(self
, filename
, source
=None):
173 # Execute an existing file
175 source
= open(filename
, "r").read()
177 code
= compile(source
, filename
, "exec")
178 except (OverflowError, SyntaxError):
179 self
.tkconsole
.resetoutput()
180 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
184 def runsource(self
, source
):
185 # Extend base class to stuff the source in the line cache first
186 filename
= self
.stuffsource(source
)
188 self
.save_warnings_filters
= warnings
.filters
[:]
189 warnings
.filterwarnings(action
="error", category
=SyntaxWarning)
191 return InteractiveInterpreter
.runsource(self
, source
, filename
)
193 if self
.save_warnings_filters
is not None:
194 warnings
.filters
[:] = self
.save_warnings_filters
195 self
.save_warnings_filters
= None
197 def stuffsource(self
, source
):
198 # Stuff source in the filename cache
199 filename
= "<pyshell#%d>" % self
.gid
200 self
.gid
= self
.gid
+ 1
201 lines
= string
.split(source
, "\n")
202 linecache
.cache
[filename
] = len(source
)+1, 0, lines
, filename
205 def showsyntaxerror(self
, filename
=None):
206 # Extend base class to color the offending position
207 # (instead of printing it and pointing at it with a caret)
208 text
= self
.tkconsole
.text
209 stuff
= self
.unpackerror()
211 self
.tkconsole
.resetoutput()
212 InteractiveInterpreter
.showsyntaxerror(self
, filename
)
214 msg
, lineno
, offset
, line
= stuff
216 pos
= "iomark + %d chars" % (offset
-1)
218 pos
= "iomark linestart + %d lines + %d chars" % (lineno
-1,
220 text
.tag_add("ERROR", pos
)
223 if char
and char
in IDENTCHARS
:
224 text
.tag_add("ERROR", pos
+ " wordstart", pos
)
225 self
.tkconsole
.resetoutput()
226 self
.write("SyntaxError: %s\n" % str(msg
))
228 def unpackerror(self
):
229 type, value
, tb
= sys
.exc_info()
230 ok
= type is SyntaxError
233 msg
, (dummy_filename
, lineno
, offset
, line
) = value
237 return msg
, lineno
, offset
, line
241 def showtraceback(self
):
242 # Extend base class method to reset output properly
243 text
= self
.tkconsole
.text
244 self
.tkconsole
.resetoutput()
245 self
.checklinecache()
246 InteractiveInterpreter
.showtraceback(self
)
248 def checklinecache(self
):
251 if key
[:1] + key
[-1:] != "<>":
256 def setdebugger(self
, debugger
):
257 self
.debugger
= debugger
259 def getdebugger(self
):
262 def runcode(self
, code
):
263 # Override base class method
264 if self
.save_warnings_filters
is not None:
265 warnings
.filters
[:] = self
.save_warnings_filters
266 self
.save_warnings_filters
= None
267 debugger
= self
.debugger
269 self
.tkconsole
.beginexecuting()
272 debugger
.run(code
, self
.locals)
274 exec code
in self
.locals
276 if tkMessageBox
.askyesno(
278 "Do you want to exit altogether?",
280 master
=self
.tkconsole
.text
):
284 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
285 self
.tkconsole
.open_stack_viewer()
288 if self
.tkconsole
.getvar("<<toggle-jit-stack-viewer>>"):
289 self
.tkconsole
.open_stack_viewer()
292 self
.tkconsole
.endexecuting()
295 # Override base class write
296 self
.tkconsole
.console
.write(s
)
299 class PyShell(OutputWindow
):
301 shell_title
= "Python Shell"
304 ColorDelegator
= ModifiedColorDelegator
305 UndoDelegator
= ModifiedUndoDelegator
307 # Override menu bar specs
308 menu_specs
= PyShellEditorWindow
.menu_specs
[:]
309 menu_specs
.insert(len(menu_specs
)-2, ("debug", "_Debug"))
312 from IdleHistory
import History
314 def __init__(self
, flist
=None):
315 self
.interp
= ModifiedInterpreter(self
)
320 flist
= PyShellFileList(root
)
322 OutputWindow
.__init
__(self
, flist
, None, None)
325 __builtin__
.quit
= __builtin__
.exit
= "To exit, type Ctrl-D."
327 self
.auto
= self
.extensions
["AutoIndent"] # Required extension
328 self
.auto
.config(usetabs
=1, indentwidth
=8, context_use_ps1
=1)
331 text
.configure(wrap
="char")
332 text
.bind("<<newline-and-indent>>", self
.enter_callback
)
333 text
.bind("<<plain-newline-and-indent>>", self
.linefeed_callback
)
334 text
.bind("<<interrupt-execution>>", self
.cancel_callback
)
335 text
.bind("<<beginning-of-line>>", self
.home_callback
)
336 text
.bind("<<end-of-file>>", self
.eof_callback
)
337 text
.bind("<<open-stack-viewer>>", self
.open_stack_viewer
)
338 text
.bind("<<toggle-debugger>>", self
.toggle_debugger
)
339 text
.bind("<<open-python-shell>>", self
.flist
.open_shell
)
340 text
.bind("<<toggle-jit-stack-viewer>>", self
.toggle_jit_stack_viewer
)
342 self
.save_stdout
= sys
.stdout
343 self
.save_stderr
= sys
.stderr
344 self
.save_stdin
= sys
.stdin
345 sys
.stdout
= PseudoFile(self
, "stdout")
346 sys
.stderr
= PseudoFile(self
, "stderr")
348 self
.console
= PseudoFile(self
, "console")
350 self
.history
= self
.History(self
.text
)
357 def toggle_debugger(self
, event
=None):
359 tkMessageBox
.showerror("Don't debug now",
360 "You can only toggle the debugger when idle",
362 self
.set_debugger_indicator()
365 db
= self
.interp
.getdebugger()
367 self
.close_debugger()
371 def set_debugger_indicator(self
):
372 db
= self
.interp
.getdebugger()
373 self
.setvar("<<toggle-debugger>>", not not db
)
375 def toggle_jit_stack_viewer( self
, event
=None):
376 pass # All we need is the variable
378 def close_debugger(self
):
379 db
= self
.interp
.getdebugger()
381 self
.interp
.setdebugger(None)
384 self
.console
.write("[DEBUG OFF]\n")
387 self
.set_debugger_indicator()
389 def open_debugger(self
):
391 self
.interp
.setdebugger(Debugger
.Debugger(self
))
392 sys
.ps1
= "[DEBUG ON]\n>>> "
394 self
.set_debugger_indicator()
396 def beginexecuting(self
):
397 # Helper for ModifiedInterpreter
400 ##self._cancel_check = self.cancel_check
401 ##sys.settrace(self._cancel_check)
403 def endexecuting(self
):
404 # Helper for ModifiedInterpreter
406 ##self._cancel_check = None
411 # Extend base class method
413 # XXX Need to ask a question here
414 if not tkMessageBox
.askokcancel(
416 "The program is still running; do you want to kill it?",
424 return OutputWindow
.close(self
)
427 self
.close_debugger()
428 # Restore std streams
429 sys
.stdout
= self
.save_stdout
430 sys
.stderr
= self
.save_stderr
431 sys
.stdin
= self
.save_stdin
436 self
.flist
.pyshell
= None
438 OutputWindow
._close
(self
) # Really EditorWindow._close
440 def ispythonsource(self
, filename
):
441 # Override this so EditorWindow never removes the colorizer
444 def short_title(self
):
445 return self
.shell_title
448 'Type "copyright", "credits" or "license" for more information.'
452 self
.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
453 (sys
.version
, sys
.platform
, self
.COPYRIGHT
,
454 idlever
.IDLE_VERSION
))
457 except AttributeError:
461 Tkinter
._default
_root
= None
474 line
= self
.text
.get("iomark", "end-1c")
478 raise KeyboardInterrupt
487 def cancel_callback(self
, event
):
489 if self
.text
.compare("sel.first", "!=", "sel.last"):
490 return # Active selection -- always use default binding
493 if not (self
.executing
or self
.reading
):
495 self
.write("KeyboardInterrupt\n")
504 def eof_callback(self
, event
):
505 if self
.executing
and not self
.reading
:
506 return # Let the default binding (delete next char) take over
507 if not (self
.text
.compare("iomark", "==", "insert") and
508 self
.text
.compare("insert", "==", "end-1c")):
509 return # Let the default binding (delete next char) take over
510 if not self
.executing
:
511 ## if not tkMessageBox.askokcancel(
513 ## "Are you sure you want to exit?",
514 ## default="ok", master=self.text):
524 def home_callback(self
, event
):
525 if event
.state
!= 0 and event
.keysym
== "Home":
526 return # <Modifier-Home>; fall back to class binding
527 if self
.text
.compare("iomark", "<=", "insert") and \
528 self
.text
.compare("insert linestart", "<=", "iomark"):
529 self
.text
.mark_set("insert", "iomark")
530 self
.text
.tag_remove("sel", "1.0", "end")
531 self
.text
.see("insert")
534 def linefeed_callback(self
, event
):
535 # Insert a linefeed without entering anything (still autoindented)
537 self
.text
.insert("insert", "\n")
538 self
.text
.see("insert")
540 self
.auto
.auto_indent(event
)
543 def enter_callback(self
, event
):
544 if self
.executing
and not self
.reading
:
545 return # Let the default binding (insert '\n') take over
546 # If some text is selected, recall the selection
547 # (but only if this before the I/O mark)
549 sel
= self
.text
.get("sel.first", "sel.last")
551 if self
.text
.compare("sel.last", "<=", "iomark"):
556 # If we're strictly before the line containing iomark, recall
557 # the current line, less a leading prompt, less leading or
558 # trailing whitespace
559 if self
.text
.compare("insert", "<", "iomark linestart"):
560 # Check if there's a relevant stdin range -- if so, use it
561 prev
= self
.text
.tag_prevrange("stdin", "insert")
562 if prev
and self
.text
.compare("insert", "<", prev
[1]):
563 self
.recall(self
.text
.get(prev
[0], prev
[1]))
565 next
= self
.text
.tag_nextrange("stdin", "insert")
566 if next
and self
.text
.compare("insert lineend", ">=", next
[0]):
567 self
.recall(self
.text
.get(next
[0], next
[1]))
569 # No stdin mark -- just get the current line
570 self
.recall(self
.text
.get("insert linestart", "insert lineend"))
572 # If we're in the current input and there's only whitespace
573 # beyond the cursor, erase that whitespace first
574 s
= self
.text
.get("insert", "end-1c")
575 if s
and not string
.strip(s
):
576 self
.text
.delete("insert", "end-1c")
577 # If we're in the current input before its last line,
578 # insert a newline right at the insert point
579 if self
.text
.compare("insert", "<", "end-1c linestart"):
580 self
.auto
.auto_indent(event
)
582 # We're in the last line; append a newline and submit it
583 self
.text
.mark_set("insert", "end-1c")
585 self
.text
.insert("insert", "\n")
586 self
.text
.see("insert")
588 self
.auto
.auto_indent(event
)
589 self
.text
.tag_add("stdin", "iomark", "end-1c")
590 self
.text
.update_idletasks()
592 self
.top
.quit() # Break out of recursive mainloop() in raw_input()
599 self
.history
.recall(s
)
602 line
= self
.text
.get("iomark", "end-1c")
603 # Strip off last newline and surrounding whitespace.
604 # (To allow you to hit return twice to end a statement.)
606 while i
> 0 and line
[i
-1] in " \t":
608 if i
> 0 and line
[i
-1] == "\n":
610 while i
> 0 and line
[i
-1] in " \t":
613 more
= self
.interp
.runsource(line
)
617 def cancel_check(self
, frame
, what
, args
,
618 dooneevent
=tkinter
.dooneevent
,
619 dontwait
=tkinter
.DONT_WAIT
):
620 # Hack -- use the debugger hooks to be able to handle events
621 # and interrupt execution at any time.
622 # This slows execution down quite a bit, so you may want to
623 # disable this (by not calling settrace() in runcode() above)
624 # for full-bore (uninterruptable) speed.
625 # XXX This should become a user option.
631 raise KeyboardInterrupt
632 return self
._cancel
_check
634 def open_stack_viewer(self
, event
=None):
638 tkMessageBox
.showerror("No stack trace",
639 "There is no stack trace yet.\n"
640 "(sys.last_traceback is not defined)",
643 from StackViewer
import StackBrowser
644 sv
= StackBrowser(self
.root
, self
.flist
)
646 def showprompt(self
):
652 self
.console
.write(s
)
653 self
.text
.mark_set("insert", "end-1c")
655 def resetoutput(self
):
656 source
= self
.text
.get("iomark", "end-1c")
658 self
.history
.history_store(source
)
659 if self
.text
.get("end-2c") != "\n":
660 self
.text
.insert("end-1c", "\n")
661 self
.text
.mark_set("iomark", "end-1c")
662 sys
.stdout
.softspace
= 0
664 def write(self
, s
, tags
=()):
665 self
.text
.mark_gravity("iomark", "right")
666 OutputWindow
.write(self
, s
, tags
, "iomark")
667 self
.text
.mark_gravity("iomark", "left")
670 raise KeyboardInterrupt
674 def __init__(self
, shell
, tags
):
679 self
.shell
.write(s
, self
.tags
)
681 def writelines(self
, l
):
692 usage: idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
694 -c command run this command
696 -e edit mode; arguments are files to be edited
697 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
698 -t title set title of shell window
700 When neither -c nor -e is used, and there are arguments, and the first
701 argument is not '-', the first argument is run as a script. Remaining
702 arguments are arguments to the script or to the command run by -c.
712 opts
, args
= getopt
.getopt(sys
.argv
[1:], "c:deist:")
713 except getopt
.error
, msg
:
714 sys
.stderr
.write("Error: %s\n" % str(msg
))
715 sys
.stderr
.write(usage_msg
)
728 PyShell
.shell_title
= a
730 for i
in range(len(sys
.path
)):
731 sys
.path
[i
] = os
.path
.abspath(sys
.path
[i
])
735 for filename
in args
:
736 pathx
.append(os
.path
.dirname(filename
))
737 elif args
and args
[0] != "-":
738 pathx
.append(os
.path
.dirname(args
[0]))
740 pathx
.append(os
.curdir
)
742 dir = os
.path
.abspath(dir)
743 if not dir in sys
.path
:
744 sys
.path
.insert(0, dir)
747 root
= Tk(className
="Idle")
750 flist
= PyShellFileList(root
)
753 for filename
in args
:
757 sys
.argv
= ["-c"] + args
759 sys
.argv
= args
or [""]
762 shell
= PyShell(flist
)
763 interp
= shell
.interp
764 flist
.pyshell
= shell
767 filename
= os
.environ
.get("IDLESTARTUP") or \
768 os
.environ
.get("PYTHONSTARTUP")
769 if filename
and os
.path
.isfile(filename
):
770 interp
.execfile(filename
)
773 shell
.open_debugger()
775 interp
.execsource(cmd
)
776 elif not edit
and args
and args
[0] != "-":
777 interp
.execfile(args
[0])
784 if __name__
== "__main__":