blob: 3e512e3067f396ed7cd56460339348c37685c610 [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001#! /usr/bin/env python
2
3# changes by dscherer@cmu.edu
4
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +00005# The main() function has been replaced by a whole class, in order to
David Scherer7aced172000-08-15 01:13:23 +00006# address the constraint that only one process can sit on the port
7# hard-coded into the loader.
8
9# It attempts to load the RPC protocol server and publish itself. If
10# that fails, it assumes that some other copy of IDLE is already running
11# on the port and attempts to contact it. It then uses the RPC mechanism
12# to ask that copy to do whatever it was instructed (via the command
13# line) to do. (Think netscape -remote). The handling of command line
14# arguments for remotes is still very incomplete.
15
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +000016# Default behavior (no command line options) is to open an editor window
17# instead of starting the Python Shell. However, if called as
18# Pyshell.main(0), the Shell will be started instead of the editor window.
David Scherer7aced172000-08-15 01:13:23 +000019
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +000020# In the default editor mode, if files are specified, they are opened.
21
22# If any command line options are specified, a shell does appear, and if
23# the -e option is used, both a shell and an editor window open.
David Scherer7aced172000-08-15 01:13:23 +000024
25import os
26import spawn
27import sys
28import string
29import getopt
30import re
31import protocol
Chui Tey5d2af632002-05-26 13:36:41 +000032import socket
33import time
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +000034import warnings
Chui Tey5d2af632002-05-26 13:36:41 +000035import traceback
David Scherer7aced172000-08-15 01:13:23 +000036
37import linecache
38from code import InteractiveInterpreter
39
40from Tkinter import *
41import tkMessageBox
42
43from EditorWindow import EditorWindow, fixwordbreaks
44from FileList import FileList
45from ColorDelegator import ColorDelegator
46from UndoDelegator import UndoDelegator
47from OutputWindow import OutputWindow, OnDemandOutputWindow
Steven M. Gava99300612001-11-04 07:03:08 +000048from configHandler import idleConf
David Scherer7aced172000-08-15 01:13:23 +000049import idlever
50
Chui Tey5d2af632002-05-26 13:36:41 +000051import rpc
52
53use_subprocess = 0 # Set to 1 to spawn subprocess for command execution
54
55# Change warnings module to write to sys.__stderr__
56try:
57 import warnings
58except ImportError:
59 pass
60else:
61 def idle_showwarning(message, category, filename, lineno):
62 file = sys.__stderr__
63 file.write(warnings.formatwarning(message, category, filename, lineno))
64 warnings.showwarning = idle_showwarning
65
David Scherer7aced172000-08-15 01:13:23 +000066# We need to patch linecache.checkcache, because we don't want it
67# to throw away our <pyshell#...> entries.
68# Rather than repeating its code here, we save those entries,
69# then call the original function, and then restore the saved entries.
70def linecache_checkcache(orig_checkcache=linecache.checkcache):
71 cache = linecache.cache
72 save = {}
73 for filename in cache.keys():
74 if filename[:1] + filename[-1:] == '<>':
75 save[filename] = cache[filename]
76 orig_checkcache()
77 cache.update(save)
78linecache.checkcache = linecache_checkcache
79
80
81# Note: <<newline-and-indent>> event is defined in AutoIndent.py
82
83#$ event <<plain-newline-and-indent>>
84#$ win <Control-j>
85#$ unix <Control-j>
86
87#$ event <<beginning-of-line>>
88#$ win <Control-a>
89#$ win <Home>
90#$ unix <Control-a>
91#$ unix <Home>
92
93#$ event <<history-next>>
94#$ win <Alt-n>
95#$ unix <Alt-n>
96
97#$ event <<history-previous>>
98#$ win <Alt-p>
99#$ unix <Alt-p>
100
101#$ event <<interrupt-execution>>
102#$ win <Control-c>
103#$ unix <Control-c>
104
105#$ event <<end-of-file>>
106#$ win <Control-d>
107#$ unix <Control-d>
108
109#$ event <<open-stack-viewer>>
110
111#$ event <<toggle-debugger>>
112
113
114class PyShellEditorWindow(EditorWindow):
115
116 # Regular text edit window when a shell is present
117 # XXX ought to merge with regular editor window
118
119 def __init__(self, *args):
120 apply(EditorWindow.__init__, (self,) + args)
121 self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
122 self.text.bind("<<open-python-shell>>", self.flist.open_shell)
123
124 rmenu_specs = [
125 ("Set breakpoint here", "<<set-breakpoint-here>>"),
126 ]
127
128 def set_breakpoint_here(self, event=None):
129 if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
130 self.text.bell()
131 return
132 self.flist.pyshell.interp.debugger.set_breakpoint_here(self)
133
134
135class PyShellFileList(FileList):
136
137 # File list when a shell is present
138
139 EditorWindow = PyShellEditorWindow
140
141 pyshell = None
142
143 def open_shell(self, event=None):
144 if self.pyshell:
145 self.pyshell.wakeup()
146 else:
147 self.pyshell = PyShell(self)
148 self.pyshell.begin()
149 return self.pyshell
150
151
152class ModifiedColorDelegator(ColorDelegator):
153
154 # Colorizer for the shell window itself
Steven M. Gavab77d3432002-03-02 07:16:21 +0000155
156 def __init__(self):
157 ColorDelegator.__init__(self)
158 self.LoadTagDefs()
David Scherer7aced172000-08-15 01:13:23 +0000159
160 def recolorize_main(self):
161 self.tag_remove("TODO", "1.0", "iomark")
162 self.tag_add("SYNC", "1.0", "iomark")
163 ColorDelegator.recolorize_main(self)
Steven M. Gavab77d3432002-03-02 07:16:21 +0000164
165 def LoadTagDefs(self):
166 ColorDelegator.LoadTagDefs(self)
167 theme = idleConf.GetOption('main','Theme','name')
168 self.tagdefs.update({
169 "stdin": {'background':None,'foreground':None},
170 "stdout": idleConf.GetHighlight(theme, "stdout"),
171 "stderr": idleConf.GetHighlight(theme, "stderr"),
172 "console": idleConf.GetHighlight(theme, "console"),
173 "ERROR": idleConf.GetHighlight(theme, "error"),
174 None: idleConf.GetHighlight(theme, "normal"),
175 })
David Scherer7aced172000-08-15 01:13:23 +0000176
David Scherer7aced172000-08-15 01:13:23 +0000177class ModifiedUndoDelegator(UndoDelegator):
178
179 # Forbid insert/delete before the I/O mark
180
181 def insert(self, index, chars, tags=None):
182 try:
183 if self.delegate.compare(index, "<", "iomark"):
184 self.delegate.bell()
185 return
186 except TclError:
187 pass
188 UndoDelegator.insert(self, index, chars, tags)
189
190 def delete(self, index1, index2=None):
191 try:
192 if self.delegate.compare(index1, "<", "iomark"):
193 self.delegate.bell()
194 return
195 except TclError:
196 pass
197 UndoDelegator.delete(self, index1, index2)
198
199class ModifiedInterpreter(InteractiveInterpreter):
200
201 def __init__(self, tkconsole):
202 self.tkconsole = tkconsole
203 locals = sys.modules['__main__'].__dict__
204 InteractiveInterpreter.__init__(self, locals=locals)
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000205 self.save_warnings_filters = None
David Scherer7aced172000-08-15 01:13:23 +0000206
Chui Tey5d2af632002-05-26 13:36:41 +0000207 global flist
208 self.output = OnDemandOutputWindow(flist)
209
210 rpcclt = None
211 rpcpid = None
212
213 def spawn_subprocess(self):
214 port = 8833
215 addr = ("localhost", port)
216 w = ['-W' + s for s in sys.warnoptions]
217 args = [sys.executable] + w + ["-c", "__import__('run').main()",
218 str(port)]
219 self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args)
220 for i in range(5):
221 time.sleep(i)
222 try:
223 self.rpcclt = rpc.RPCClient(addr)
224 break
225 except socket.error, err:
226 if i > 3:
227 print >>sys.__stderr__, "Socket error:", err, "; retry..."
228 else:
229 # XXX Make this a dialog?
230 print >>sys.__stderr__, "Can't spawn subprocess!"
231 return
232 self.output.stdout=PseudoFile(self.output, "stdout")
233 self.output.stderr=PseudoFile(self.output, "stderr")
234 self.rpcclt.register("stdin", self.output)
235 self.rpcclt.register("stdout", self.output.stdout)
236 self.rpcclt.register("stderr", self.output.stderr)
237 self.rpcclt.register("flist", self.tkconsole.flist)
238 self.poll_subprocess()
239
240 active_seq = None
241
242 def poll_subprocess(self):
243 clt = self.rpcclt
244 if clt is None:
245 return
246 response = clt.pollresponse(self.active_seq)
247 self.tkconsole.text.after(50, self.poll_subprocess)
248 if response:
249 self.tkconsole.resetoutput()
250 self.active_seq = None
251 how, what = response
252 file = self.tkconsole.console
253 if how == "OK":
254 if what is not None:
255 print >>file, `what`
256 elif how == "EXCEPTION":
257 mod, name, args, tb = what
258 print >>file, 'Traceback (most recent call last):'
259 while tb and tb[0][0] in ("run.py", "rpc.py"):
260 del tb[0]
261 while tb and tb[-1][0] in ("run.py", "rpc.py"):
262 del tb[-1]
263 for i in range(len(tb)):
264 fn, ln, nm, line = tb[i]
265 if not line and fn.startswith("<pyshell#"):
266 line = linecache.getline(fn, ln)
267 tb[i] = fn, ln, nm, line
268 traceback.print_list(tb, file=file)
269 if mod and mod != "exceptions":
270 name = mod + "." + name
271 print >>file, name + ":", " ".join(map(str, args))
272 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
273 self.remote_stack_viewer()
274 elif how == "ERROR":
275 print >>sys.__stderr__, "Oops:", how, what
276 print >>file, "Oops:", how, what
277 self.tkconsole.endexecuting()
278
279 def kill_subprocess(self):
280 clt = self.rpcclt
281 self.rpcclt = None
282 if clt is not None:
283 clt.close()
284
285 def remote_stack_viewer(self):
286 import RemoteObjectBrowser
287 oid = self.rpcclt.remotecall("exec", "stackviewer", ("flist",), {})
288 if oid is None:
289 self.tkconsole.root.bell()
290 return
291 item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid)
292 from TreeWidget import ScrolledCanvas, TreeNode
293 top = Toplevel(self.tkconsole.root)
294 sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
295 sc.frame.pack(expand=1, fill="both")
296 node = TreeNode(sc.canvas, None, item)
297 node.expand()
298 # XXX Should GC the remote tree when closing the window
299
David Scherer7aced172000-08-15 01:13:23 +0000300 gid = 0
301
302 def execsource(self, source):
303 # Like runsource() but assumes complete exec source
304 filename = self.stuffsource(source)
305 self.execfile(filename, source)
306
307 def execfile(self, filename, source=None):
308 # Execute an existing file
309 if source is None:
310 source = open(filename, "r").read()
311 try:
312 code = compile(source, filename, "exec")
313 except (OverflowError, SyntaxError):
314 self.tkconsole.resetoutput()
315 InteractiveInterpreter.showsyntaxerror(self, filename)
316 else:
317 self.runcode(code)
318
319 def runsource(self, source):
320 # Extend base class to stuff the source in the line cache first
321 filename = self.stuffsource(source)
322 self.more = 0
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000323 self.save_warnings_filters = warnings.filters[:]
324 warnings.filterwarnings(action="error", category=SyntaxWarning)
325 try:
326 return InteractiveInterpreter.runsource(self, source, filename)
327 finally:
328 if self.save_warnings_filters is not None:
329 warnings.filters[:] = self.save_warnings_filters
330 self.save_warnings_filters = None
David Scherer7aced172000-08-15 01:13:23 +0000331
332 def stuffsource(self, source):
333 # Stuff source in the filename cache
334 filename = "<pyshell#%d>" % self.gid
335 self.gid = self.gid + 1
336 lines = string.split(source, "\n")
337 linecache.cache[filename] = len(source)+1, 0, lines, filename
338 return filename
339
340 def showsyntaxerror(self, filename=None):
341 # Extend base class to color the offending position
342 # (instead of printing it and pointing at it with a caret)
343 text = self.tkconsole.text
344 stuff = self.unpackerror()
345 if not stuff:
346 self.tkconsole.resetoutput()
347 InteractiveInterpreter.showsyntaxerror(self, filename)
348 return
349 msg, lineno, offset, line = stuff
350 if lineno == 1:
351 pos = "iomark + %d chars" % (offset-1)
352 else:
353 pos = "iomark linestart + %d lines + %d chars" % (lineno-1,
354 offset-1)
355 text.tag_add("ERROR", pos)
356 text.see(pos)
357 char = text.get(pos)
358 if char and char in string.letters + string.digits + "_":
359 text.tag_add("ERROR", pos + " wordstart", pos)
360 self.tkconsole.resetoutput()
361 self.write("SyntaxError: %s\n" % str(msg))
362
363 def unpackerror(self):
364 type, value, tb = sys.exc_info()
365 ok = type is SyntaxError
366 if ok:
367 try:
368 msg, (dummy_filename, lineno, offset, line) = value
369 except:
370 ok = 0
371 if ok:
372 return msg, lineno, offset, line
373 else:
374 return None
375
376 def showtraceback(self):
377 # Extend base class method to reset output properly
David Scherer7aced172000-08-15 01:13:23 +0000378 self.tkconsole.resetoutput()
379 self.checklinecache()
380 InteractiveInterpreter.showtraceback(self)
Chui Tey5d2af632002-05-26 13:36:41 +0000381 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
382 self.tkconsole.open_stack_viewer()
David Scherer7aced172000-08-15 01:13:23 +0000383
384 def checklinecache(self):
385 c = linecache.cache
386 for key in c.keys():
387 if key[:1] + key[-1:] != "<>":
388 del c[key]
389
390 debugger = None
391
392 def setdebugger(self, debugger):
393 self.debugger = debugger
394
395 def getdebugger(self):
396 return self.debugger
397
Chui Tey5d2af632002-05-26 13:36:41 +0000398 def runcommand(self, code):
399 # This runs the code without invoking the debugger.
400 # The code better not raise an exception!
401 if self.tkconsole.executing:
402 tkMessageBox.showerror(
403 "Already executing",
404 "The Python Shell window is already executing a command; "
405 "please wait until it is finished.",
406 master=self.tkconsole.text)
407 return 0
408 if self.rpcclt:
409 self.rpcclt.remotecall("exec", "runcode", (code,), {})
410 else:
411 exec code in self.locals
412 return 1
413
David Scherer7aced172000-08-15 01:13:23 +0000414 def runcode(self, code):
415 # Override base class method
Chui Tey5d2af632002-05-26 13:36:41 +0000416 if self.tkconsole.executing:
417 tkMessageBox.showerror(
418 "Already executing",
419 "The Python Shell window is already executing a command; "
420 "please wait until it is finished.",
421 master=self.tkconsole.text)
422 return
423
424 self.checklinecache()
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000425 if self.save_warnings_filters is not None:
426 warnings.filters[:] = self.save_warnings_filters
427 self.save_warnings_filters = None
David Scherer7aced172000-08-15 01:13:23 +0000428 debugger = self.debugger
Chui Tey5d2af632002-05-26 13:36:41 +0000429 if not debugger and self.rpcclt is not None:
430 self.tkconsole.beginexecuting()
431 self.active_seq = self.rpcclt.asynccall("exec", "runcode",
432 (code,), {})
433 return
434
David Scherer7aced172000-08-15 01:13:23 +0000435 try:
436 self.tkconsole.beginexecuting()
437 try:
438 if debugger:
439 debugger.run(code, self.locals)
440 else:
441 exec code in self.locals
442 except SystemExit:
443 if tkMessageBox.askyesno(
444 "Exit?",
445 "Do you want to exit altogether?",
446 default="yes",
447 master=self.tkconsole.text):
448 raise
449 else:
450 self.showtraceback()
David Scherer7aced172000-08-15 01:13:23 +0000451 except:
452 self.showtraceback()
David Scherer7aced172000-08-15 01:13:23 +0000453
454 finally:
455 self.tkconsole.endexecuting()
456
457 def write(self, s):
458 # Override base class write
459 self.tkconsole.console.write(s)
460
David Scherer7aced172000-08-15 01:13:23 +0000461class PyShell(OutputWindow):
462
463 shell_title = "Python Shell"
464
465 # Override classes
466 ColorDelegator = ModifiedColorDelegator
467 UndoDelegator = ModifiedUndoDelegator
468
469 # Override menu bar specs
470 menu_specs = PyShellEditorWindow.menu_specs[:]
Steven M. Gava3b55a892001-11-21 05:56:26 +0000471 menu_specs.insert(len(menu_specs)-3, ("debug", "_Debug"))
David Scherer7aced172000-08-15 01:13:23 +0000472
473 # New classes
474 from IdleHistory import History
475
476 def __init__(self, flist=None):
477 self.interp = ModifiedInterpreter(self)
478 if flist is None:
479 root = Tk()
480 fixwordbreaks(root)
481 root.withdraw()
482 flist = PyShellFileList(root)
483
484 OutputWindow.__init__(self, flist, None, None)
485
486 import __builtin__
487 __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
488
489 self.auto = self.extensions["AutoIndent"] # Required extension
490 self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1)
491
492 text = self.text
493 text.configure(wrap="char")
494 text.bind("<<newline-and-indent>>", self.enter_callback)
495 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
496 text.bind("<<interrupt-execution>>", self.cancel_callback)
497 text.bind("<<beginning-of-line>>", self.home_callback)
498 text.bind("<<end-of-file>>", self.eof_callback)
499 text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
500 text.bind("<<toggle-debugger>>", self.toggle_debugger)
501 text.bind("<<open-python-shell>>", self.flist.open_shell)
502 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
503
504 self.save_stdout = sys.stdout
505 self.save_stderr = sys.stderr
506 self.save_stdin = sys.stdin
Chui Tey5d2af632002-05-26 13:36:41 +0000507 self.stdout = PseudoFile(self, "stdout")
508 self.stderr = PseudoFile(self, "stderr")
David Scherer7aced172000-08-15 01:13:23 +0000509 self.console = PseudoFile(self, "console")
Chui Tey5d2af632002-05-26 13:36:41 +0000510 if not use_subprocess:
511 sys.stdout = self.stdout
512 sys.stderr = self.stderr
513 sys.stdin = self
David Scherer7aced172000-08-15 01:13:23 +0000514
515 self.history = self.History(self.text)
516
Chui Tey5d2af632002-05-26 13:36:41 +0000517 if use_subprocess:
518 self.interp.spawn_subprocess()
519
David Scherer7aced172000-08-15 01:13:23 +0000520 reading = 0
521 executing = 0
522 canceled = 0
523 endoffile = 0
524
525 def toggle_debugger(self, event=None):
526 if self.executing:
527 tkMessageBox.showerror("Don't debug now",
528 "You can only toggle the debugger when idle",
529 master=self.text)
530 self.set_debugger_indicator()
531 return "break"
532 else:
533 db = self.interp.getdebugger()
534 if db:
535 self.close_debugger()
536 else:
537 self.open_debugger()
538
539 def set_debugger_indicator(self):
540 db = self.interp.getdebugger()
541 self.setvar("<<toggle-debugger>>", not not db)
542
543 def toggle_jit_stack_viewer( self, event=None):
544 pass # All we need is the variable
545
546 def close_debugger(self):
547 db = self.interp.getdebugger()
548 if db:
549 self.interp.setdebugger(None)
550 db.close()
551 self.resetoutput()
552 self.console.write("[DEBUG OFF]\n")
553 sys.ps1 = ">>> "
554 self.showprompt()
555 self.set_debugger_indicator()
556
557 def open_debugger(self):
Chui Tey5d2af632002-05-26 13:36:41 +0000558 if self.interp.rpcclt:
559 return self.open_remote_debugger()
David Scherer7aced172000-08-15 01:13:23 +0000560 import Debugger
561 self.interp.setdebugger(Debugger.Debugger(self))
562 sys.ps1 = "[DEBUG ON]\n>>> "
563 self.showprompt()
564 self.set_debugger_indicator()
565
Chui Tey5d2af632002-05-26 13:36:41 +0000566 def open_remote_debugger(self):
567 import RemoteDebugger
568 gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt, self)
569 self.interp.setdebugger(gui)
570 sys.ps1 = "[DEBUG ON]\n>>> "
571 self.showprompt()
572 self.set_debugger_indicator()
573
David Scherer7aced172000-08-15 01:13:23 +0000574 def beginexecuting(self):
575 # Helper for ModifiedInterpreter
576 self.resetoutput()
577 self.executing = 1
578 ##self._cancel_check = self.cancel_check
579 ##sys.settrace(self._cancel_check)
580
581 def endexecuting(self):
582 # Helper for ModifiedInterpreter
583 ##sys.settrace(None)
584 ##self._cancel_check = None
585 self.executing = 0
586 self.canceled = 0
Chui Tey5d2af632002-05-26 13:36:41 +0000587 self.showprompt()
David Scherer7aced172000-08-15 01:13:23 +0000588
589 def close(self):
590 # Extend base class method
591 if self.executing:
592 # XXX Need to ask a question here
593 if not tkMessageBox.askokcancel(
594 "Kill?",
595 "The program is still running; do you want to kill it?",
596 default="ok",
597 master=self.text):
598 return "cancel"
599 self.canceled = 1
600 if self.reading:
601 self.top.quit()
602 return "cancel"
Kurt B. Kaiserbb6b1e92001-07-14 05:10:34 +0000603 return OutputWindow.close(self)
David Scherer7aced172000-08-15 01:13:23 +0000604
605 def _close(self):
606 self.close_debugger()
Chui Tey5d2af632002-05-26 13:36:41 +0000607 self.interp.kill_subprocess()
David Scherer7aced172000-08-15 01:13:23 +0000608 # Restore std streams
609 sys.stdout = self.save_stdout
610 sys.stderr = self.save_stderr
611 sys.stdin = self.save_stdin
612 # Break cycles
613 self.interp = None
614 self.console = None
615 self.auto = None
616 self.flist.pyshell = None
617 self.history = None
618 OutputWindow._close(self) # Really EditorWindow._close
619
620 def ispythonsource(self, filename):
621 # Override this so EditorWindow never removes the colorizer
622 return 1
623
624 def short_title(self):
625 return self.shell_title
626
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000627 COPYRIGHT = \
628 'Type "copyright", "credits" or "license" for more information.'
629
David Scherer7aced172000-08-15 01:13:23 +0000630 def begin(self):
631 self.resetoutput()
Kurt B. Kaisere75785a2001-07-16 05:25:12 +0000632 self.write("Python %s on %s\n%s\nIDLE Fork %s -- press F1 for help\n" %
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000633 (sys.version, sys.platform, self.COPYRIGHT,
David Scherer7aced172000-08-15 01:13:23 +0000634 idlever.IDLE_VERSION))
635 try:
636 sys.ps1
637 except AttributeError:
638 sys.ps1 = ">>> "
639 self.showprompt()
640 import Tkinter
641 Tkinter._default_root = None
642
643 def interact(self):
644 self.begin()
645 self.top.mainloop()
646
647 def readline(self):
648 save = self.reading
649 try:
650 self.reading = 1
651 self.top.mainloop()
652 finally:
653 self.reading = save
654 line = self.text.get("iomark", "end-1c")
655 self.resetoutput()
656 if self.canceled:
657 self.canceled = 0
658 raise KeyboardInterrupt
659 if self.endoffile:
660 self.endoffile = 0
661 return ""
662 return line
663
664 def isatty(self):
665 return 1
666
667 def cancel_callback(self, event):
668 try:
669 if self.text.compare("sel.first", "!=", "sel.last"):
670 return # Active selection -- always use default binding
671 except:
672 pass
673 if not (self.executing or self.reading):
674 self.resetoutput()
675 self.write("KeyboardInterrupt\n")
676 self.showprompt()
677 return "break"
678 self.endoffile = 0
David Scherer7aced172000-08-15 01:13:23 +0000679 if self.reading:
Chui Tey5d2af632002-05-26 13:36:41 +0000680 self.canceled = 1
David Scherer7aced172000-08-15 01:13:23 +0000681 self.top.quit()
Chui Tey5d2af632002-05-26 13:36:41 +0000682 elif (self.executing and self.interp.rpcclt and
683 self.interp.rpcpid and hasattr(os, "kill")):
684 try:
685 from signal import SIGINT
686 except ImportError:
687 SIGINT = 2
688 os.kill(self.interp.rpcpid, SIGINT)
689 else:
690 self.canceled = 1
David Scherer7aced172000-08-15 01:13:23 +0000691 return "break"
692
693 def eof_callback(self, event):
694 if self.executing and not self.reading:
695 return # Let the default binding (delete next char) take over
696 if not (self.text.compare("iomark", "==", "insert") and
697 self.text.compare("insert", "==", "end-1c")):
698 return # Let the default binding (delete next char) take over
699 if not self.executing:
David Scherer7aced172000-08-15 01:13:23 +0000700 self.resetoutput()
701 self.close()
702 else:
703 self.canceled = 0
704 self.endoffile = 1
705 self.top.quit()
706 return "break"
707
708 def home_callback(self, event):
709 if event.state != 0 and event.keysym == "Home":
710 return # <Modifier-Home>; fall back to class binding
711 if self.text.compare("iomark", "<=", "insert") and \
712 self.text.compare("insert linestart", "<=", "iomark"):
713 self.text.mark_set("insert", "iomark")
714 self.text.tag_remove("sel", "1.0", "end")
715 self.text.see("insert")
716 return "break"
717
718 def linefeed_callback(self, event):
719 # Insert a linefeed without entering anything (still autoindented)
720 if self.reading:
721 self.text.insert("insert", "\n")
722 self.text.see("insert")
723 else:
724 self.auto.auto_indent(event)
725 return "break"
726
727 def enter_callback(self, event):
728 if self.executing and not self.reading:
729 return # Let the default binding (insert '\n') take over
730 # If some text is selected, recall the selection
731 # (but only if this before the I/O mark)
732 try:
733 sel = self.text.get("sel.first", "sel.last")
734 if sel:
735 if self.text.compare("sel.last", "<=", "iomark"):
736 self.recall(sel)
737 return "break"
738 except:
739 pass
740 # If we're strictly before the line containing iomark, recall
741 # the current line, less a leading prompt, less leading or
742 # trailing whitespace
743 if self.text.compare("insert", "<", "iomark linestart"):
744 # Check if there's a relevant stdin range -- if so, use it
745 prev = self.text.tag_prevrange("stdin", "insert")
746 if prev and self.text.compare("insert", "<", prev[1]):
747 self.recall(self.text.get(prev[0], prev[1]))
748 return "break"
749 next = self.text.tag_nextrange("stdin", "insert")
750 if next and self.text.compare("insert lineend", ">=", next[0]):
751 self.recall(self.text.get(next[0], next[1]))
752 return "break"
753 # No stdin mark -- just get the current line
754 self.recall(self.text.get("insert linestart", "insert lineend"))
755 return "break"
756 # If we're in the current input and there's only whitespace
757 # beyond the cursor, erase that whitespace first
758 s = self.text.get("insert", "end-1c")
759 if s and not string.strip(s):
760 self.text.delete("insert", "end-1c")
761 # If we're in the current input before its last line,
762 # insert a newline right at the insert point
763 if self.text.compare("insert", "<", "end-1c linestart"):
764 self.auto.auto_indent(event)
765 return "break"
766 # We're in the last line; append a newline and submit it
767 self.text.mark_set("insert", "end-1c")
768 if self.reading:
769 self.text.insert("insert", "\n")
770 self.text.see("insert")
771 else:
772 self.auto.auto_indent(event)
773 self.text.tag_add("stdin", "iomark", "end-1c")
774 self.text.update_idletasks()
775 if self.reading:
776 self.top.quit() # Break out of recursive mainloop() in raw_input()
777 else:
778 self.runit()
779 return "break"
780
781 def recall(self, s):
782 if self.history:
783 self.history.recall(s)
784
785 def runit(self):
786 line = self.text.get("iomark", "end-1c")
787 # Strip off last newline and surrounding whitespace.
788 # (To allow you to hit return twice to end a statement.)
789 i = len(line)
790 while i > 0 and line[i-1] in " \t":
791 i = i-1
792 if i > 0 and line[i-1] == "\n":
793 i = i-1
794 while i > 0 and line[i-1] in " \t":
795 i = i-1
796 line = line[:i]
797 more = self.interp.runsource(line)
798 if not more:
799 self.showprompt()
800
801 def cancel_check(self, frame, what, args,
802 dooneevent=tkinter.dooneevent,
803 dontwait=tkinter.DONT_WAIT):
804 # Hack -- use the debugger hooks to be able to handle events
805 # and interrupt execution at any time.
806 # This slows execution down quite a bit, so you may want to
807 # disable this (by not calling settrace() in runcode() above)
808 # for full-bore (uninterruptable) speed.
809 # XXX This should become a user option.
810 if self.canceled:
811 return
812 dooneevent(dontwait)
813 if self.canceled:
814 self.canceled = 0
815 raise KeyboardInterrupt
816 return self._cancel_check
817
818 def open_stack_viewer(self, event=None):
Chui Tey5d2af632002-05-26 13:36:41 +0000819 if self.interp.rpcclt:
820 return self.interp.remote_stack_viewer()
David Scherer7aced172000-08-15 01:13:23 +0000821 try:
822 sys.last_traceback
823 except:
824 tkMessageBox.showerror("No stack trace",
825 "There is no stack trace yet.\n"
826 "(sys.last_traceback is not defined)",
827 master=self.text)
828 return
829 from StackViewer import StackBrowser
830 sv = StackBrowser(self.root, self.flist)
831
832 def showprompt(self):
833 self.resetoutput()
834 try:
835 s = str(sys.ps1)
836 except:
837 s = ""
838 self.console.write(s)
839 self.text.mark_set("insert", "end-1c")
Chui Tey5d2af632002-05-26 13:36:41 +0000840 self.set_line_and_column()
David Scherer7aced172000-08-15 01:13:23 +0000841
842 def resetoutput(self):
843 source = self.text.get("iomark", "end-1c")
844 if self.history:
845 self.history.history_store(source)
846 if self.text.get("end-2c") != "\n":
847 self.text.insert("end-1c", "\n")
848 self.text.mark_set("iomark", "end-1c")
Chui Tey5d2af632002-05-26 13:36:41 +0000849 self.set_line_and_column()
David Scherer7aced172000-08-15 01:13:23 +0000850 sys.stdout.softspace = 0
851
852 def write(self, s, tags=()):
853 self.text.mark_gravity("iomark", "right")
854 OutputWindow.write(self, s, tags, "iomark")
855 self.text.mark_gravity("iomark", "left")
856 if self.canceled:
857 self.canceled = 0
858 raise KeyboardInterrupt
859
860class PseudoFile:
861
862 def __init__(self, shell, tags):
863 self.shell = shell
864 self.tags = tags
Chui Tey5d2af632002-05-26 13:36:41 +0000865 self.softspace = 0
David Scherer7aced172000-08-15 01:13:23 +0000866
867 def write(self, s):
868 self.shell.write(s, self.tags)
869
870 def writelines(self, l):
871 map(self.write, l)
872
873 def flush(self):
874 pass
875
876 def isatty(self):
877 return 1
878
879usage_msg = """\
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000880usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] [arg] ...
David Scherer7aced172000-08-15 01:13:23 +0000881
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000882idle file(s) (without options) edit the file(s)
David Scherer7aced172000-08-15 01:13:23 +0000883
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000884-c cmd run the command in a shell
885-d enable the debugger
Chui Tey5d2af632002-05-26 13:36:41 +0000886-e edit mode; arguments are files to be edited
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000887-i open an interactive shell
888-i file(s) open a shell and also an editor window for each file
Chui Tey5d2af632002-05-26 13:36:41 +0000889-r script use experimental remote (subprocess) execution feature
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000890-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
891-t title set title of shell window
892
893Remaining arguments are applied to the command (-c) or script (-r).
David Scherer7aced172000-08-15 01:13:23 +0000894"""
895
896class usageError:
897 def __init__(self, string): self.string = string
898 def __repr__(self): return self.string
899
900class main:
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +0000901 def __init__(self, noshell=1):
Steven M. Gava1f733ba2001-10-07 11:44:49 +0000902
903 global flist, root
904 root = Tk(className="Idle")
905 fixwordbreaks(root)
906 root.withdraw()
907 flist = PyShellFileList(root)
908
Steven M. Gavacedd30b2002-03-27 00:51:53 +0000909 # the following causes lockups and silent failures when debugging
910 # changes to EditorWindow.__init__ ; the console works fine for idle
911 # debugging in any case, so disable this unnescesary stuff.
912 #dbg=OnDemandOutputWindow(flist)
913 #dbg.set_title('IDLE Debugging Messages')
914 #sys.stdout = PseudoFile(dbg,['stdout'])
915 #sys.stderr = PseudoFile(dbg,['stderr'])
Steven M. Gava1f733ba2001-10-07 11:44:49 +0000916
David Scherer7aced172000-08-15 01:13:23 +0000917 try:
918 self.server = protocol.Server(connection_hook = self.address_ok)
919 protocol.publish( 'IDLE', self.connect )
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +0000920 self.main(sys.argv[1:], noshell)
David Scherer7aced172000-08-15 01:13:23 +0000921 return
922 except protocol.connectionLost:
923 try:
924 client = protocol.Client()
925 IDLE = client.getobject('IDLE')
926 if IDLE:
927 try:
928 IDLE.remote( sys.argv[1:] )
929 except usageError, msg:
930 sys.stderr.write("Error: %s\n" % str(msg))
931 sys.stderr.write(usage_msg)
932 return
933 except protocol.connectionLost:
934 pass
935
Steven M. Gava1f733ba2001-10-07 11:44:49 +0000936 #maybe the following should be handled by a tkmessagebox for
937 #users who don't start idle from a console??
938 print """\
939IDLE cannot run.
940
941IDLE needs to use a specific TCP/IP port (7454) in order to execute and
942debug programs. IDLE is unable to bind to this port, and so cannot
943start. Here are some possible causes of this problem:
944
945 1. TCP/IP networking is not installed or not working on this computer
946 2. Another program is running that uses this port
947 3. Another copy of IDLE stopped responding but is still bound to the port
948 4. Personal firewall software is preventing IDLE from using this port
949
950IDLE makes and accepts connections only with this computer, and does not
951communicate over the internet in any way. It's use of port 7454 should not
952be a security risk on a single-user machine.
953"""
954 dbg.owin.gotoline(1)
955 dbg.owin.remove_selection()
956 root.mainloop() # wait for user to read message
David Scherer7aced172000-08-15 01:13:23 +0000957
958 def idle(self):
959 spawn.kill_zombies()
960 self.server.rpc_loop()
961 root.after(25, self.idle)
962
963 # We permit connections from localhost only
964 def address_ok(self, addr):
965 return addr[0] == '127.0.0.1'
966
967 def connect(self, client, addr):
968 return self
969
970 def remote( self, argv ):
971 # xxx Should make this behavior match the behavior in main, or redo
972 # command line options entirely.
973
974 try:
975 opts, args = getopt.getopt(argv, "c:deist:")
976 except getopt.error, msg:
977 raise usageError(msg)
978
979 for filename in args:
980 flist.open(filename)
981 if not args:
982 flist.new()
983
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +0000984 def main(self, argv, noshell):
David Scherer7aced172000-08-15 01:13:23 +0000985 cmd = None
986 edit = 0
David Scherer7aced172000-08-15 01:13:23 +0000987 debug = 0
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000988 interactive = 0
989 script = None
David Scherer7aced172000-08-15 01:13:23 +0000990 startup = 0
Chui Tey5d2af632002-05-26 13:36:41 +0000991 global use_subprocess
David Scherer7aced172000-08-15 01:13:23 +0000992
993 try:
Chui Tey5d2af632002-05-26 13:36:41 +0000994 opts, args = getopt.getopt(sys.argv[1:], "c:deir:st:")
David Scherer7aced172000-08-15 01:13:23 +0000995 except getopt.error, msg:
996 sys.stderr.write("Error: %s\n" % str(msg))
997 sys.stderr.write(usage_msg)
998 sys.exit(2)
999
1000 for o, a in opts:
Kurt B. Kaiser96d88422001-07-17 04:59:01 +00001001 noshell = 0 # There are options, bring up a shell
David Scherer7aced172000-08-15 01:13:23 +00001002 if o == '-c':
1003 cmd = a
1004 if o == '-d':
1005 debug = 1
Chui Tey5d2af632002-05-26 13:36:41 +00001006 if o == '-e':
1007 edit = 1
Kurt B. Kaiser96d88422001-07-17 04:59:01 +00001008 if o == '-i':
1009 interactive = 1
1010 if o == '-r':
Chui Tey5d2af632002-05-26 13:36:41 +00001011 edit = 1
Kurt B. Kaiser96d88422001-07-17 04:59:01 +00001012 script = a
Chui Tey5d2af632002-05-26 13:36:41 +00001013 use_subprocess = 1
David Scherer7aced172000-08-15 01:13:23 +00001014 if o == '-s':
1015 startup = 1
1016 if o == '-t':
1017 PyShell.shell_title = a
1018
1019 if noshell: edit=1
Kurt B. Kaiser96d88422001-07-17 04:59:01 +00001020 if interactive and args and args[0] != "-": edit = 1
David Scherer7aced172000-08-15 01:13:23 +00001021
David Scherer7aced172000-08-15 01:13:23 +00001022 for i in range(len(sys.path)):
1023 sys.path[i] = os.path.abspath(sys.path[i])
1024
1025 pathx = []
1026 if edit:
1027 for filename in args:
1028 pathx.append(os.path.dirname(filename))
1029 elif args and args[0] != "-":
1030 pathx.append(os.path.dirname(args[0]))
1031 else:
1032 pathx.append(os.curdir)
1033 for dir in pathx:
1034 dir = os.path.abspath(dir)
1035 if not dir in sys.path:
1036 sys.path.insert(0, dir)
1037
David Scherer7aced172000-08-15 01:13:23 +00001038 if edit:
1039 for filename in args:
1040 flist.open(filename)
1041 if not args:
1042 flist.new()
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +00001043 else:
1044 if cmd:
1045 sys.argv = ["-c"] + args
1046 else:
1047 sys.argv = args or [""]
1048
David Scherer7aced172000-08-15 01:13:23 +00001049 if noshell:
1050 flist.pyshell = None
1051 else:
1052 shell = PyShell(flist)
1053 interp = shell.interp
1054 flist.pyshell = shell
1055
1056 if startup:
1057 filename = os.environ.get("IDLESTARTUP") or \
1058 os.environ.get("PYTHONSTARTUP")
1059 if filename and os.path.isfile(filename):
1060 interp.execfile(filename)
1061
1062 if debug:
1063 shell.open_debugger()
1064 if cmd:
1065 interp.execsource(cmd)
Kurt B. Kaiser96d88422001-07-17 04:59:01 +00001066 elif script:
1067 if os.path.isfile(script):
1068 interp.execfile(script)
1069 else:
1070 print "No script file: ", script
David Scherer7aced172000-08-15 01:13:23 +00001071 shell.begin()
1072
1073 self.idle()
1074 root.mainloop()
1075 root.destroy()
1076
1077
1078if __name__ == "__main__":
1079 main()