blob: f349bd6c0db535ae6ba0729ca879536ca0abaa69 [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
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +000032import warnings
David Scherer7aced172000-08-15 01:13:23 +000033
34import linecache
35from code import InteractiveInterpreter
36
37from Tkinter import *
38import tkMessageBox
39
40from EditorWindow import EditorWindow, fixwordbreaks
41from FileList import FileList
42from ColorDelegator import ColorDelegator
43from UndoDelegator import UndoDelegator
44from OutputWindow import OutputWindow, OnDemandOutputWindow
45from IdleConf import idleconf
Steven M. Gava99300612001-11-04 07:03:08 +000046from configHandler import idleConf
David Scherer7aced172000-08-15 01:13:23 +000047import idlever
48
49# We need to patch linecache.checkcache, because we don't want it
50# to throw away our <pyshell#...> entries.
51# Rather than repeating its code here, we save those entries,
52# then call the original function, and then restore the saved entries.
53def linecache_checkcache(orig_checkcache=linecache.checkcache):
54 cache = linecache.cache
55 save = {}
56 for filename in cache.keys():
57 if filename[:1] + filename[-1:] == '<>':
58 save[filename] = cache[filename]
59 orig_checkcache()
60 cache.update(save)
61linecache.checkcache = linecache_checkcache
62
63
64# Note: <<newline-and-indent>> event is defined in AutoIndent.py
65
66#$ event <<plain-newline-and-indent>>
67#$ win <Control-j>
68#$ unix <Control-j>
69
70#$ event <<beginning-of-line>>
71#$ win <Control-a>
72#$ win <Home>
73#$ unix <Control-a>
74#$ unix <Home>
75
76#$ event <<history-next>>
77#$ win <Alt-n>
78#$ unix <Alt-n>
79
80#$ event <<history-previous>>
81#$ win <Alt-p>
82#$ unix <Alt-p>
83
84#$ event <<interrupt-execution>>
85#$ win <Control-c>
86#$ unix <Control-c>
87
88#$ event <<end-of-file>>
89#$ win <Control-d>
90#$ unix <Control-d>
91
92#$ event <<open-stack-viewer>>
93
94#$ event <<toggle-debugger>>
95
96
97class PyShellEditorWindow(EditorWindow):
98
99 # Regular text edit window when a shell is present
100 # XXX ought to merge with regular editor window
101
102 def __init__(self, *args):
103 apply(EditorWindow.__init__, (self,) + args)
104 self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
105 self.text.bind("<<open-python-shell>>", self.flist.open_shell)
106
107 rmenu_specs = [
108 ("Set breakpoint here", "<<set-breakpoint-here>>"),
109 ]
110
111 def set_breakpoint_here(self, event=None):
112 if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
113 self.text.bell()
114 return
115 self.flist.pyshell.interp.debugger.set_breakpoint_here(self)
116
117
118class PyShellFileList(FileList):
119
120 # File list when a shell is present
121
122 EditorWindow = PyShellEditorWindow
123
124 pyshell = None
125
126 def open_shell(self, event=None):
127 if self.pyshell:
128 self.pyshell.wakeup()
129 else:
130 self.pyshell = PyShell(self)
131 self.pyshell.begin()
132 return self.pyshell
133
134
135class ModifiedColorDelegator(ColorDelegator):
136
137 # Colorizer for the shell window itself
138
139 def recolorize_main(self):
140 self.tag_remove("TODO", "1.0", "iomark")
141 self.tag_add("SYNC", "1.0", "iomark")
142 ColorDelegator.recolorize_main(self)
143
144 tagdefs = ColorDelegator.tagdefs.copy()
Steven M. Gava99300612001-11-04 07:03:08 +0000145 theme = idleConf.GetOption('main','Theme','name')
David Scherer7aced172000-08-15 01:13:23 +0000146 tagdefs.update({
Steven M. Gava99300612001-11-04 07:03:08 +0000147
148 "stdin": idleConf.GetHighlight(theme, "stdin"),
149 "stdout": idleConf.GetHighlight(theme, "stdout"),
150 "stderr": idleConf.GetHighlight(theme, "stderr"),
151 "console": idleConf.GetHighlight(theme, "console"),
152 "ERROR": idleConf.GetHighlight(theme, "error"),
153 None: idleConf.GetHighlight(theme, "normal"),
David Scherer7aced172000-08-15 01:13:23 +0000154 })
155
156
157class ModifiedUndoDelegator(UndoDelegator):
158
159 # Forbid insert/delete before the I/O mark
160
161 def insert(self, index, chars, tags=None):
162 try:
163 if self.delegate.compare(index, "<", "iomark"):
164 self.delegate.bell()
165 return
166 except TclError:
167 pass
168 UndoDelegator.insert(self, index, chars, tags)
169
170 def delete(self, index1, index2=None):
171 try:
172 if self.delegate.compare(index1, "<", "iomark"):
173 self.delegate.bell()
174 return
175 except TclError:
176 pass
177 UndoDelegator.delete(self, index1, index2)
178
179class ModifiedInterpreter(InteractiveInterpreter):
180
181 def __init__(self, tkconsole):
182 self.tkconsole = tkconsole
183 locals = sys.modules['__main__'].__dict__
184 InteractiveInterpreter.__init__(self, locals=locals)
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000185 self.save_warnings_filters = None
David Scherer7aced172000-08-15 01:13:23 +0000186
187 gid = 0
188
189 def execsource(self, source):
190 # Like runsource() but assumes complete exec source
191 filename = self.stuffsource(source)
192 self.execfile(filename, source)
193
194 def execfile(self, filename, source=None):
195 # Execute an existing file
196 if source is None:
197 source = open(filename, "r").read()
198 try:
199 code = compile(source, filename, "exec")
200 except (OverflowError, SyntaxError):
201 self.tkconsole.resetoutput()
202 InteractiveInterpreter.showsyntaxerror(self, filename)
203 else:
204 self.runcode(code)
205
206 def runsource(self, source):
207 # Extend base class to stuff the source in the line cache first
208 filename = self.stuffsource(source)
209 self.more = 0
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000210 self.save_warnings_filters = warnings.filters[:]
211 warnings.filterwarnings(action="error", category=SyntaxWarning)
212 try:
213 return InteractiveInterpreter.runsource(self, source, filename)
214 finally:
215 if self.save_warnings_filters is not None:
216 warnings.filters[:] = self.save_warnings_filters
217 self.save_warnings_filters = None
David Scherer7aced172000-08-15 01:13:23 +0000218
219 def stuffsource(self, source):
220 # Stuff source in the filename cache
221 filename = "<pyshell#%d>" % self.gid
222 self.gid = self.gid + 1
223 lines = string.split(source, "\n")
224 linecache.cache[filename] = len(source)+1, 0, lines, filename
225 return filename
226
227 def showsyntaxerror(self, filename=None):
228 # Extend base class to color the offending position
229 # (instead of printing it and pointing at it with a caret)
230 text = self.tkconsole.text
231 stuff = self.unpackerror()
232 if not stuff:
233 self.tkconsole.resetoutput()
234 InteractiveInterpreter.showsyntaxerror(self, filename)
235 return
236 msg, lineno, offset, line = stuff
237 if lineno == 1:
238 pos = "iomark + %d chars" % (offset-1)
239 else:
240 pos = "iomark linestart + %d lines + %d chars" % (lineno-1,
241 offset-1)
242 text.tag_add("ERROR", pos)
243 text.see(pos)
244 char = text.get(pos)
245 if char and char in string.letters + string.digits + "_":
246 text.tag_add("ERROR", pos + " wordstart", pos)
247 self.tkconsole.resetoutput()
248 self.write("SyntaxError: %s\n" % str(msg))
249
250 def unpackerror(self):
251 type, value, tb = sys.exc_info()
252 ok = type is SyntaxError
253 if ok:
254 try:
255 msg, (dummy_filename, lineno, offset, line) = value
256 except:
257 ok = 0
258 if ok:
259 return msg, lineno, offset, line
260 else:
261 return None
262
263 def showtraceback(self):
264 # Extend base class method to reset output properly
265 text = self.tkconsole.text
266 self.tkconsole.resetoutput()
267 self.checklinecache()
268 InteractiveInterpreter.showtraceback(self)
269
270 def checklinecache(self):
271 c = linecache.cache
272 for key in c.keys():
273 if key[:1] + key[-1:] != "<>":
274 del c[key]
275
276 debugger = None
277
278 def setdebugger(self, debugger):
279 self.debugger = debugger
280
281 def getdebugger(self):
282 return self.debugger
283
284 def runcode(self, code):
285 # Override base class method
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000286 if self.save_warnings_filters is not None:
287 warnings.filters[:] = self.save_warnings_filters
288 self.save_warnings_filters = None
David Scherer7aced172000-08-15 01:13:23 +0000289 debugger = self.debugger
290 try:
291 self.tkconsole.beginexecuting()
292 try:
293 if debugger:
294 debugger.run(code, self.locals)
295 else:
296 exec code in self.locals
297 except SystemExit:
298 if tkMessageBox.askyesno(
299 "Exit?",
300 "Do you want to exit altogether?",
301 default="yes",
302 master=self.tkconsole.text):
303 raise
304 else:
305 self.showtraceback()
306 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
307 self.tkconsole.open_stack_viewer()
308 except:
309 self.showtraceback()
310 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
311 self.tkconsole.open_stack_viewer()
312
313 finally:
314 self.tkconsole.endexecuting()
315
316 def write(self, s):
317 # Override base class write
318 self.tkconsole.console.write(s)
319
320
321class PyShell(OutputWindow):
322
323 shell_title = "Python Shell"
324
325 # Override classes
326 ColorDelegator = ModifiedColorDelegator
327 UndoDelegator = ModifiedUndoDelegator
328
329 # Override menu bar specs
330 menu_specs = PyShellEditorWindow.menu_specs[:]
331 menu_specs.insert(len(menu_specs)-2, ("debug", "_Debug"))
332
333 # New classes
334 from IdleHistory import History
335
336 def __init__(self, flist=None):
337 self.interp = ModifiedInterpreter(self)
338 if flist is None:
339 root = Tk()
340 fixwordbreaks(root)
341 root.withdraw()
342 flist = PyShellFileList(root)
343
344 OutputWindow.__init__(self, flist, None, None)
345
346 import __builtin__
347 __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
348
349 self.auto = self.extensions["AutoIndent"] # Required extension
350 self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1)
351
352 text = self.text
353 text.configure(wrap="char")
354 text.bind("<<newline-and-indent>>", self.enter_callback)
355 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
356 text.bind("<<interrupt-execution>>", self.cancel_callback)
357 text.bind("<<beginning-of-line>>", self.home_callback)
358 text.bind("<<end-of-file>>", self.eof_callback)
359 text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
360 text.bind("<<toggle-debugger>>", self.toggle_debugger)
361 text.bind("<<open-python-shell>>", self.flist.open_shell)
362 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
363
364 self.save_stdout = sys.stdout
365 self.save_stderr = sys.stderr
366 self.save_stdin = sys.stdin
367 sys.stdout = PseudoFile(self, "stdout")
368 sys.stderr = PseudoFile(self, "stderr")
369 sys.stdin = self
370 self.console = PseudoFile(self, "console")
371
372 self.history = self.History(self.text)
373
374 reading = 0
375 executing = 0
376 canceled = 0
377 endoffile = 0
378
379 def toggle_debugger(self, event=None):
380 if self.executing:
381 tkMessageBox.showerror("Don't debug now",
382 "You can only toggle the debugger when idle",
383 master=self.text)
384 self.set_debugger_indicator()
385 return "break"
386 else:
387 db = self.interp.getdebugger()
388 if db:
389 self.close_debugger()
390 else:
391 self.open_debugger()
392
393 def set_debugger_indicator(self):
394 db = self.interp.getdebugger()
395 self.setvar("<<toggle-debugger>>", not not db)
396
397 def toggle_jit_stack_viewer( self, event=None):
398 pass # All we need is the variable
399
400 def close_debugger(self):
401 db = self.interp.getdebugger()
402 if db:
403 self.interp.setdebugger(None)
404 db.close()
405 self.resetoutput()
406 self.console.write("[DEBUG OFF]\n")
407 sys.ps1 = ">>> "
408 self.showprompt()
409 self.set_debugger_indicator()
410
411 def open_debugger(self):
412 import Debugger
413 self.interp.setdebugger(Debugger.Debugger(self))
414 sys.ps1 = "[DEBUG ON]\n>>> "
415 self.showprompt()
416 self.set_debugger_indicator()
417
418 def beginexecuting(self):
419 # Helper for ModifiedInterpreter
420 self.resetoutput()
421 self.executing = 1
422 ##self._cancel_check = self.cancel_check
423 ##sys.settrace(self._cancel_check)
424
425 def endexecuting(self):
426 # Helper for ModifiedInterpreter
427 ##sys.settrace(None)
428 ##self._cancel_check = None
429 self.executing = 0
430 self.canceled = 0
431
432 def close(self):
433 # Extend base class method
434 if self.executing:
435 # XXX Need to ask a question here
436 if not tkMessageBox.askokcancel(
437 "Kill?",
438 "The program is still running; do you want to kill it?",
439 default="ok",
440 master=self.text):
441 return "cancel"
442 self.canceled = 1
443 if self.reading:
444 self.top.quit()
445 return "cancel"
Kurt B. Kaiserbb6b1e92001-07-14 05:10:34 +0000446 return OutputWindow.close(self)
David Scherer7aced172000-08-15 01:13:23 +0000447
448 def _close(self):
449 self.close_debugger()
450 # Restore std streams
451 sys.stdout = self.save_stdout
452 sys.stderr = self.save_stderr
453 sys.stdin = self.save_stdin
454 # Break cycles
455 self.interp = None
456 self.console = None
457 self.auto = None
458 self.flist.pyshell = None
459 self.history = None
460 OutputWindow._close(self) # Really EditorWindow._close
461
462 def ispythonsource(self, filename):
463 # Override this so EditorWindow never removes the colorizer
464 return 1
465
466 def short_title(self):
467 return self.shell_title
468
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000469 COPYRIGHT = \
470 'Type "copyright", "credits" or "license" for more information.'
471
David Scherer7aced172000-08-15 01:13:23 +0000472 def begin(self):
473 self.resetoutput()
Kurt B. Kaisere75785a2001-07-16 05:25:12 +0000474 self.write("Python %s on %s\n%s\nIDLE Fork %s -- press F1 for help\n" %
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000475 (sys.version, sys.platform, self.COPYRIGHT,
David Scherer7aced172000-08-15 01:13:23 +0000476 idlever.IDLE_VERSION))
477 try:
478 sys.ps1
479 except AttributeError:
480 sys.ps1 = ">>> "
481 self.showprompt()
482 import Tkinter
483 Tkinter._default_root = None
484
485 def interact(self):
486 self.begin()
487 self.top.mainloop()
488
489 def readline(self):
490 save = self.reading
491 try:
492 self.reading = 1
493 self.top.mainloop()
494 finally:
495 self.reading = save
496 line = self.text.get("iomark", "end-1c")
497 self.resetoutput()
498 if self.canceled:
499 self.canceled = 0
500 raise KeyboardInterrupt
501 if self.endoffile:
502 self.endoffile = 0
503 return ""
504 return line
505
506 def isatty(self):
507 return 1
508
509 def cancel_callback(self, event):
510 try:
511 if self.text.compare("sel.first", "!=", "sel.last"):
512 return # Active selection -- always use default binding
513 except:
514 pass
515 if not (self.executing or self.reading):
516 self.resetoutput()
517 self.write("KeyboardInterrupt\n")
518 self.showprompt()
519 return "break"
520 self.endoffile = 0
521 self.canceled = 1
522 if self.reading:
523 self.top.quit()
524 return "break"
525
526 def eof_callback(self, event):
527 if self.executing and not self.reading:
528 return # Let the default binding (delete next char) take over
529 if not (self.text.compare("iomark", "==", "insert") and
530 self.text.compare("insert", "==", "end-1c")):
531 return # Let the default binding (delete next char) take over
532 if not self.executing:
533## if not tkMessageBox.askokcancel(
534## "Exit?",
535## "Are you sure you want to exit?",
536## default="ok", master=self.text):
537## return "break"
538 self.resetoutput()
539 self.close()
540 else:
541 self.canceled = 0
542 self.endoffile = 1
543 self.top.quit()
544 return "break"
545
546 def home_callback(self, event):
547 if event.state != 0 and event.keysym == "Home":
548 return # <Modifier-Home>; fall back to class binding
549 if self.text.compare("iomark", "<=", "insert") and \
550 self.text.compare("insert linestart", "<=", "iomark"):
551 self.text.mark_set("insert", "iomark")
552 self.text.tag_remove("sel", "1.0", "end")
553 self.text.see("insert")
554 return "break"
555
556 def linefeed_callback(self, event):
557 # Insert a linefeed without entering anything (still autoindented)
558 if self.reading:
559 self.text.insert("insert", "\n")
560 self.text.see("insert")
561 else:
562 self.auto.auto_indent(event)
563 return "break"
564
565 def enter_callback(self, event):
566 if self.executing and not self.reading:
567 return # Let the default binding (insert '\n') take over
568 # If some text is selected, recall the selection
569 # (but only if this before the I/O mark)
570 try:
571 sel = self.text.get("sel.first", "sel.last")
572 if sel:
573 if self.text.compare("sel.last", "<=", "iomark"):
574 self.recall(sel)
575 return "break"
576 except:
577 pass
578 # If we're strictly before the line containing iomark, recall
579 # the current line, less a leading prompt, less leading or
580 # trailing whitespace
581 if self.text.compare("insert", "<", "iomark linestart"):
582 # Check if there's a relevant stdin range -- if so, use it
583 prev = self.text.tag_prevrange("stdin", "insert")
584 if prev and self.text.compare("insert", "<", prev[1]):
585 self.recall(self.text.get(prev[0], prev[1]))
586 return "break"
587 next = self.text.tag_nextrange("stdin", "insert")
588 if next and self.text.compare("insert lineend", ">=", next[0]):
589 self.recall(self.text.get(next[0], next[1]))
590 return "break"
591 # No stdin mark -- just get the current line
592 self.recall(self.text.get("insert linestart", "insert lineend"))
593 return "break"
594 # If we're in the current input and there's only whitespace
595 # beyond the cursor, erase that whitespace first
596 s = self.text.get("insert", "end-1c")
597 if s and not string.strip(s):
598 self.text.delete("insert", "end-1c")
599 # If we're in the current input before its last line,
600 # insert a newline right at the insert point
601 if self.text.compare("insert", "<", "end-1c linestart"):
602 self.auto.auto_indent(event)
603 return "break"
604 # We're in the last line; append a newline and submit it
605 self.text.mark_set("insert", "end-1c")
606 if self.reading:
607 self.text.insert("insert", "\n")
608 self.text.see("insert")
609 else:
610 self.auto.auto_indent(event)
611 self.text.tag_add("stdin", "iomark", "end-1c")
612 self.text.update_idletasks()
613 if self.reading:
614 self.top.quit() # Break out of recursive mainloop() in raw_input()
615 else:
616 self.runit()
617 return "break"
618
619 def recall(self, s):
620 if self.history:
621 self.history.recall(s)
622
623 def runit(self):
624 line = self.text.get("iomark", "end-1c")
625 # Strip off last newline and surrounding whitespace.
626 # (To allow you to hit return twice to end a statement.)
627 i = len(line)
628 while i > 0 and line[i-1] in " \t":
629 i = i-1
630 if i > 0 and line[i-1] == "\n":
631 i = i-1
632 while i > 0 and line[i-1] in " \t":
633 i = i-1
634 line = line[:i]
635 more = self.interp.runsource(line)
636 if not more:
637 self.showprompt()
638
639 def cancel_check(self, frame, what, args,
640 dooneevent=tkinter.dooneevent,
641 dontwait=tkinter.DONT_WAIT):
642 # Hack -- use the debugger hooks to be able to handle events
643 # and interrupt execution at any time.
644 # This slows execution down quite a bit, so you may want to
645 # disable this (by not calling settrace() in runcode() above)
646 # for full-bore (uninterruptable) speed.
647 # XXX This should become a user option.
648 if self.canceled:
649 return
650 dooneevent(dontwait)
651 if self.canceled:
652 self.canceled = 0
653 raise KeyboardInterrupt
654 return self._cancel_check
655
656 def open_stack_viewer(self, event=None):
657 try:
658 sys.last_traceback
659 except:
660 tkMessageBox.showerror("No stack trace",
661 "There is no stack trace yet.\n"
662 "(sys.last_traceback is not defined)",
663 master=self.text)
664 return
665 from StackViewer import StackBrowser
666 sv = StackBrowser(self.root, self.flist)
667
668 def showprompt(self):
669 self.resetoutput()
670 try:
671 s = str(sys.ps1)
672 except:
673 s = ""
674 self.console.write(s)
675 self.text.mark_set("insert", "end-1c")
676
677 def resetoutput(self):
678 source = self.text.get("iomark", "end-1c")
679 if self.history:
680 self.history.history_store(source)
681 if self.text.get("end-2c") != "\n":
682 self.text.insert("end-1c", "\n")
683 self.text.mark_set("iomark", "end-1c")
684 sys.stdout.softspace = 0
685
686 def write(self, s, tags=()):
687 self.text.mark_gravity("iomark", "right")
688 OutputWindow.write(self, s, tags, "iomark")
689 self.text.mark_gravity("iomark", "left")
690 if self.canceled:
691 self.canceled = 0
692 raise KeyboardInterrupt
693
694class PseudoFile:
695
696 def __init__(self, shell, tags):
697 self.shell = shell
698 self.tags = tags
699
700 def write(self, s):
701 self.shell.write(s, self.tags)
702
703 def writelines(self, l):
704 map(self.write, l)
705
706 def flush(self):
707 pass
708
709 def isatty(self):
710 return 1
711
712usage_msg = """\
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000713usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] [arg] ...
David Scherer7aced172000-08-15 01:13:23 +0000714
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000715idle file(s) (without options) edit the file(s)
David Scherer7aced172000-08-15 01:13:23 +0000716
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000717-c cmd run the command in a shell
718-d enable the debugger
719-i open an interactive shell
720-i file(s) open a shell and also an editor window for each file
721-r script run a file as a script in a shell
722-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
723-t title set title of shell window
724
725Remaining arguments are applied to the command (-c) or script (-r).
David Scherer7aced172000-08-15 01:13:23 +0000726"""
727
728class usageError:
729 def __init__(self, string): self.string = string
730 def __repr__(self): return self.string
731
732class main:
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +0000733 def __init__(self, noshell=1):
Steven M. Gava1f733ba2001-10-07 11:44:49 +0000734
735 global flist, root
736 root = Tk(className="Idle")
737 fixwordbreaks(root)
738 root.withdraw()
739 flist = PyShellFileList(root)
740
741 dbg=OnDemandOutputWindow(flist)
742 dbg.set_title('IDLE Debugging Messages')
743 sys.stdout = PseudoFile(dbg,['stdout'])
744 sys.stderr = PseudoFile(dbg,['stderr'])
745
David Scherer7aced172000-08-15 01:13:23 +0000746 try:
747 self.server = protocol.Server(connection_hook = self.address_ok)
748 protocol.publish( 'IDLE', self.connect )
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +0000749 self.main(sys.argv[1:], noshell)
David Scherer7aced172000-08-15 01:13:23 +0000750 return
751 except protocol.connectionLost:
752 try:
753 client = protocol.Client()
754 IDLE = client.getobject('IDLE')
755 if IDLE:
756 try:
757 IDLE.remote( sys.argv[1:] )
758 except usageError, msg:
759 sys.stderr.write("Error: %s\n" % str(msg))
760 sys.stderr.write(usage_msg)
761 return
762 except protocol.connectionLost:
763 pass
764
Steven M. Gava1f733ba2001-10-07 11:44:49 +0000765 #maybe the following should be handled by a tkmessagebox for
766 #users who don't start idle from a console??
767 print """\
768IDLE cannot run.
769
770IDLE needs to use a specific TCP/IP port (7454) in order to execute and
771debug programs. IDLE is unable to bind to this port, and so cannot
772start. Here are some possible causes of this problem:
773
774 1. TCP/IP networking is not installed or not working on this computer
775 2. Another program is running that uses this port
776 3. Another copy of IDLE stopped responding but is still bound to the port
777 4. Personal firewall software is preventing IDLE from using this port
778
779IDLE makes and accepts connections only with this computer, and does not
780communicate over the internet in any way. It's use of port 7454 should not
781be a security risk on a single-user machine.
782"""
783 dbg.owin.gotoline(1)
784 dbg.owin.remove_selection()
785 root.mainloop() # wait for user to read message
David Scherer7aced172000-08-15 01:13:23 +0000786
787 def idle(self):
788 spawn.kill_zombies()
789 self.server.rpc_loop()
790 root.after(25, self.idle)
791
792 # We permit connections from localhost only
793 def address_ok(self, addr):
794 return addr[0] == '127.0.0.1'
795
796 def connect(self, client, addr):
797 return self
798
799 def remote( self, argv ):
800 # xxx Should make this behavior match the behavior in main, or redo
801 # command line options entirely.
802
803 try:
804 opts, args = getopt.getopt(argv, "c:deist:")
805 except getopt.error, msg:
806 raise usageError(msg)
807
808 for filename in args:
809 flist.open(filename)
810 if not args:
811 flist.new()
812
Kurt B. Kaiser21ebb212001-07-16 04:00:10 +0000813 def main(self, argv, noshell):
David Scherer7aced172000-08-15 01:13:23 +0000814 cmd = None
815 edit = 0
David Scherer7aced172000-08-15 01:13:23 +0000816 debug = 0
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000817 interactive = 0
818 script = None
David Scherer7aced172000-08-15 01:13:23 +0000819 startup = 0
820
821 try:
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000822 opts, args = getopt.getopt(argv, "c:dir:st:")
David Scherer7aced172000-08-15 01:13:23 +0000823 except getopt.error, msg:
824 sys.stderr.write("Error: %s\n" % str(msg))
825 sys.stderr.write(usage_msg)
826 sys.exit(2)
827
828 for o, a in opts:
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000829 noshell = 0 # There are options, bring up a shell
David Scherer7aced172000-08-15 01:13:23 +0000830 if o == '-c':
831 cmd = a
832 if o == '-d':
833 debug = 1
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000834 if o == '-i':
835 interactive = 1
836 if o == '-r':
837 script = a
David Scherer7aced172000-08-15 01:13:23 +0000838 if o == '-s':
839 startup = 1
840 if o == '-t':
841 PyShell.shell_title = a
842
843 if noshell: edit=1
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000844 if interactive and args and args[0] != "-": edit = 1
David Scherer7aced172000-08-15 01:13:23 +0000845
David Scherer7aced172000-08-15 01:13:23 +0000846 for i in range(len(sys.path)):
847 sys.path[i] = os.path.abspath(sys.path[i])
848
849 pathx = []
850 if edit:
851 for filename in args:
852 pathx.append(os.path.dirname(filename))
853 elif args and args[0] != "-":
854 pathx.append(os.path.dirname(args[0]))
855 else:
856 pathx.append(os.curdir)
857 for dir in pathx:
858 dir = os.path.abspath(dir)
859 if not dir in sys.path:
860 sys.path.insert(0, dir)
861
David Scherer7aced172000-08-15 01:13:23 +0000862 if edit:
863 for filename in args:
864 flist.open(filename)
865 if not args:
866 flist.new()
Kurt B. Kaiser94bd7742001-07-14 00:13:28 +0000867 else:
868 if cmd:
869 sys.argv = ["-c"] + args
870 else:
871 sys.argv = args or [""]
872
David Scherer7aced172000-08-15 01:13:23 +0000873 if noshell:
874 flist.pyshell = None
875 else:
876 shell = PyShell(flist)
877 interp = shell.interp
878 flist.pyshell = shell
879
880 if startup:
881 filename = os.environ.get("IDLESTARTUP") or \
882 os.environ.get("PYTHONSTARTUP")
883 if filename and os.path.isfile(filename):
884 interp.execfile(filename)
885
886 if debug:
887 shell.open_debugger()
888 if cmd:
889 interp.execsource(cmd)
Kurt B. Kaiser96d88422001-07-17 04:59:01 +0000890 elif script:
891 if os.path.isfile(script):
892 interp.execfile(script)
893 else:
894 print "No script file: ", script
David Scherer7aced172000-08-15 01:13:23 +0000895 shell.begin()
896
897 self.idle()
898 root.mainloop()
899 root.destroy()
900
901
902if __name__ == "__main__":
903 main()