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