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