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