From 969de458aa12e831942637bbcd9994b29dc86252 Mon Sep 17 00:00:00 2001 From: "Kurt B. Kaiser" Date: Wed, 12 Jun 2002 03:28:57 +0000 Subject: Rework the code to have only the GvR RPC. Output from execution of user code is directed to the Shell. --- Lib/idlelib/EditorWindow.py | 6 - Lib/idlelib/ExecBinding.py | 193 -------------------- Lib/idlelib/OutputWindow.py | 214 +++++----------------- Lib/idlelib/PyShell.py | 315 +++++++++++--------------------- Lib/idlelib/Remote.py | 101 ----------- Lib/idlelib/ScriptBinding.py | 17 +- Lib/idlelib/config-extensions.def | 20 +-- Lib/idlelib/config.txt | 4 +- Lib/idlelib/loader.py | 64 ------- Lib/idlelib/protocol.py | 369 -------------------------------------- Lib/idlelib/spawn.py | 58 ------ 11 files changed, 171 insertions(+), 1190 deletions(-) delete mode 100644 Lib/idlelib/ExecBinding.py delete mode 100644 Lib/idlelib/Remote.py delete mode 100644 Lib/idlelib/loader.py delete mode 100644 Lib/idlelib/protocol.py delete mode 100644 Lib/idlelib/spawn.py diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 4f3c945..be074b0 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -1,9 +1,3 @@ -# changes by dscherer@cmu.edu -# - created format and run menus -# - added silly advice dialog (apologies to Douglas Adams) -# - made Python Documentation work on Windows (requires win32api to -# do a ShellExecute(); other ways of starting a web browser are awkward) - import sys import os import string diff --git a/Lib/idlelib/ExecBinding.py b/Lib/idlelib/ExecBinding.py deleted file mode 100644 index e148769..0000000 --- a/Lib/idlelib/ExecBinding.py +++ /dev/null @@ -1,193 +0,0 @@ -"""Extension to execute a script in a separate process - -David Scherer - - The ExecBinding module, a replacement for ScriptBinding, executes - programs in a separate process. Unlike previous versions, this version - communicates with the user process via an RPC protocol (see the 'protocol' - module). The user program is loaded by the 'loader' and 'Remote' - modules. Its standard output and input are directed back to the - ExecBinding class through the RPC mechanism and implemented here. - - A "stop program" command is provided and bound to control-break. Closing - the output window also stops the running program. -""" - -import sys -import os -import imp -import OutputWindow -import protocol -import spawn -import traceback -import tempfile - -# Find Python and the loader. This should be done as early in execution -# as possible, because if the current directory or sys.path is changed -# it may no longer be possible to get correct paths for these things. - -pyth_exe = spawn.hardpath( sys.executable ) -load_py = spawn.hardpath( imp.find_module("loader")[1] ) - -# The following mechanism matches loaders up with ExecBindings that are -# trying to load something. - -waiting_for_loader = [] - -def loader_connect(client, addr): - if waiting_for_loader: - a = waiting_for_loader.pop(0) - try: - return a.connect(client, addr) - except: - return loader_connect(client,addr) - -protocol.publish('ExecBinding', loader_connect) - -class ExecBinding: - menudefs = [ - ('run', [None, - ('Run program', '<>'), - ('Stop program', '<>'), - ] - ), - ] - - delegate = 1 - - def __init__(self, editwin): - self.editwin = editwin - self.client = None - self.temp = [] - - if not hasattr(editwin, 'source_window'): - self.delegate = 0 - self.output = OutputWindow.OnDemandOutputWindow(editwin.flist) - self.output.close_hook = self.stopProgram - self.output.source_window = editwin - else: - if (self.editwin.source_window and - self.editwin.source_window.extensions.has_key('ExecBinding') and - not self.editwin.source_window.extensions['ExecBinding'].delegate): - delegate = self.editwin.source_window.extensions['ExecBinding'] - self.run_complete_script_event = delegate.run_complete_script_event - self.stop_execution_event = delegate.stop_execution_event - - def __del__(self): - self.stopProgram() - - def stop_execution_event(self, event): - if self.client: - self.stopProgram() - self.write('\nProgram stopped.\n','stderr') - - def run_complete_script_event(self, event): - filename = self.getfilename() - if not filename: return - filename = os.path.abspath(filename) - - self.stopProgram() - - self.commands = [ ('run', filename) ] - waiting_for_loader.append(self) - spawn.spawn( pyth_exe, load_py ) - - def connect(self, client, addr): - # Called by loader_connect() above. It is remotely possible that - # we get connected to two loaders if the user is running the - # program repeatedly in a short span of time. In this case, we - # simply return None, refusing to connect and letting the redundant - # loader die. - if self.client: return None - - self.client = client - client.set_close_hook( self.connect_lost ) - - title = self.editwin.short_title() - if title: - self.output.set_title(title + " Output") - else: - self.output.set_title("Output") - self.output.write('\n',"stderr") - self.output.scroll_clear() - - return self - - def connect_lost(self): - # Called by the client's close hook when the loader closes its - # socket. - - # We print a disconnect message only if the output window is already - # open. - if self.output.owin and self.output.owin.text: - self.output.owin.interrupt() - self.output.write("\nProgram disconnected.\n","stderr") - - for t in self.temp: - try: - os.remove(t) - except: - pass - self.temp = [] - self.client = None - - def get_command(self): - # Called by Remote to find out what it should be executing. - # Later this will be used to implement debugging, interactivity, etc. - if self.commands: - return self.commands.pop(0) - return ('finish',) - - def program_exception(self, type, value, tb, first, last): - if type == SystemExit: return 0 - - for i in range(len(tb)): - filename, lineno, name, line = tb[i] - if filename in self.temp: - filename = 'Untitled' - tb[i] = filename, lineno, name, line - - list = traceback.format_list(tb[first:last]) - exc = traceback.format_exception_only( type, value ) - - self.write('Traceback (innermost last)\n', 'stderr') - for i in (list+exc): - self.write(i, 'stderr') - - self.commands = [] - return 1 - - def write(self, text, tag): - self.output.write(text,tag) - - def readline(self): - return self.output.readline() - - def stopProgram(self): - if self.client: - self.client.close() - self.client = None - - def getfilename(self): - # Save all files which have been named, because they might be modules - for edit in self.editwin.flist.inversedict.keys(): - if edit.io and edit.io.filename and not edit.get_saved(): - edit.io.save(None) - - # Experimental: execute unnamed buffer - if not self.editwin.io.filename: - filename = os.path.normcase(os.path.abspath(tempfile.mktemp())) - self.temp.append(filename) - if self.editwin.io.writefile(filename): - return filename - - # If the file isn't save, we save it. If it doesn't have a filename, - # the user will be prompted. - if self.editwin.io and not self.editwin.get_saved(): - self.editwin.io.save(None) - - # If the file *still* isn't saved, we give up. - if not self.editwin.get_saved(): - return - - return self.editwin.io.filename diff --git a/Lib/idlelib/OutputWindow.py b/Lib/idlelib/OutputWindow.py index ed6774b..212e382 100644 --- a/Lib/idlelib/OutputWindow.py +++ b/Lib/idlelib/OutputWindow.py @@ -1,64 +1,19 @@ -# changes by dscherer@cmu.edu -# - OutputWindow and OnDemandOutputWindow have been hastily -# extended to provide readline() support, an "iomark" separate -# from the "insert" cursor, and scrolling to clear the window. -# These changes are used by the ExecBinding module to provide -# standard input and output for user programs. Many of the new -# features are very similar to features of PyShell, which is a -# subclass of OutputWindow. Someone should make some sense of -# this. - from Tkinter import * from EditorWindow import EditorWindow import re import tkMessageBox -from UndoDelegator import UndoDelegator +class OutputWindow(EditorWindow): -class OutputUndoDelegator(UndoDelegator): - reading = 0 - # Forbid insert/delete before the I/O mark, in the blank lines after - # the output, or *anywhere* if we are not presently doing user input - def insert(self, index, chars, tags=None): - try: - if (self.delegate.compare(index, "<", "iomark") or - self.delegate.compare(index, ">", "endmark") or - (index!="iomark" and not self.reading)): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.insert(self, index, chars, tags) - def delete(self, index1, index2=None): - try: - if (self.delegate.compare(index1, "<", "iomark") or - self.delegate.compare(index1, ">", "endmark") or - (index2 and self.delegate.compare(index2, ">=", "endmark")) or - not self.reading): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.delete(self, index1, index2) + """An editor window that can serve as an output file. -class OutputWindow(EditorWindow): - """An editor window that can serve as an input and output file. - The input support has been rather hastily hacked in, and should - not be trusted. + Also the future base class for the Python shell window. + This class has no input facilities. """ - UndoDelegator = OutputUndoDelegator - source_window = None - - def __init__(self, *args, **keywords): - if keywords.has_key('source_window'): - self.source_window = keywords['source_window'] + def __init__(self, *args): apply(EditorWindow.__init__, (self,) + args) self.text.bind("<>", self.goto_file_line) - self.text.bind("<>", self.enter_callback) - self.text.mark_set("iomark","1.0") - self.text.mark_gravity("iomark", LEFT) - self.text.mark_set("endmark","1.0") # Customize EditorWindow @@ -69,9 +24,6 @@ class OutputWindow(EditorWindow): def short_title(self): return "Output" - def long_title(self): - return "" - def maybesave(self): # Override base class method -- don't ask any questions if self.get_saved(): @@ -79,63 +31,10 @@ class OutputWindow(EditorWindow): else: return "no" - # Act as input file - incomplete - - def set_line_and_column(self, event=None): - index = self.text.index(INSERT) - if (self.text.compare(index, ">", "endmark")): - self.text.mark_set("insert", "endmark") - self.text.see("insert") - EditorWindow.set_line_and_column(self) - - reading = 0 - canceled = 0 - endoffile = 0 - - def readline(self): - save = self.reading - try: - self.reading = self.undo.reading = 1 - self.text.mark_set("insert", "iomark") - self.text.see("insert") - self.top.mainloop() - finally: - self.reading = self.undo.reading = save - line = self.text.get("input", "iomark") - if self.canceled: - self.canceled = 0 - raise KeyboardInterrupt - if self.endoffile: - self.endoffile = 0 - return "" - return line or '\n' - - def close(self): - self.interrupt() - return EditorWindow.close(self) - - def interrupt(self): - if self.reading: - self.endoffile = 1 - self.top.quit() - - def enter_callback(self, event): - if self.reading and self.text.compare("insert", ">=", "iomark"): - self.text.mark_set("input", "iomark") - self.text.mark_set("iomark", "insert") - self.write('\n',"iomark") - self.text.tag_add("stdin", "input", "iomark") - self.text.update_idletasks() - self.top.quit() # Break out of recursive mainloop() in raw_input() - - return "break" - # Act as output file - def write(self, s, tags=(), mark="iomark"): - self.text.mark_gravity(mark, RIGHT) - self.text.insert(mark, s, tags) - self.text.mark_gravity(mark, LEFT) + def write(self, s, tags=(), mark="insert"): + self.text.insert(mark, str(s), tags) self.text.see(mark) self.text.update() @@ -183,14 +82,8 @@ class OutputWindow(EditorWindow): master=self.text) return filename, lineno = result - edit = self.untitled(filename) or self.flist.open(filename) + edit = self.flist.open(filename) edit.gotoline(lineno) - edit.wakeup() - - def untitled(self, filename): - if filename!='Untitled' or not self.source_window or self.source_window.io.filename: - return None - return self.source_window def _file_line_helper(self, line): for prog in self.file_line_progs: @@ -200,80 +93,63 @@ class OutputWindow(EditorWindow): else: return None filename, lineno = m.group(1, 2) - if not self.untitled(filename): - try: - f = open(filename, "r") - f.close() - except IOError: - return None + try: + f = open(filename, "r") + f.close() + except IOError: + return None try: return filename, int(lineno) except TypeError: return None -# This classes now used by ExecBinding.py: +# These classes are currently not used but might come in handy class OnDemandOutputWindow: - source_window = None tagdefs = { # XXX Should use IdlePrefs.ColorPrefs - "stdin": {"foreground": "black"}, "stdout": {"foreground": "blue"}, - "stderr": {"foreground": "red"}, - } - + "stderr": {"foreground": "#007700"}, + } + def __init__(self, flist): self.flist = flist self.owin = None - self.title = "Output" - self.close_hook = None - self.old_close = None - - def owclose(self): - if self.close_hook: - self.close_hook() - if self.old_close: - self.old_close() - - def set_title(self, title): - self.title = title - if self.owin and self.owin.text: - self.owin.saved_change_hook() - - def write(self, s, tags=(), mark="iomark"): - if not self.owin or not self.owin.text: + + def write(self, s, tags, mark): + if not self.owin: self.setup() self.owin.write(s, tags, mark) - def readline(self): - if not self.owin or not self.owin.text: - self.setup() - return self.owin.readline() - - def scroll_clear(self): - if self.owin and self.owin.text: - lineno = self.owin.getlineno("endmark") - self.owin.text.mark_set("insert","endmark") - self.owin.text.yview(float(lineno)) - self.owin.wakeup() - def setup(self): - self.owin = owin = OutputWindow(self.flist, source_window = self.source_window) - owin.short_title = lambda self=self: self.title + self.owin = owin = OutputWindow(self.flist) text = owin.text - - self.old_close = owin.close_hook - owin.close_hook = self.owclose - - # xxx Bad hack: 50 blank lines at the bottom so that - # we can scroll the top of the window to the output - # cursor in scroll_clear(). There must be a better way... - owin.text.mark_gravity('endmark', LEFT) - owin.text.insert('iomark', '\n'*50) - owin.text.mark_gravity('endmark', RIGHT) - for tag, cnf in self.tagdefs.items(): if cnf: apply(text.tag_configure, (tag,), cnf) text.tag_raise('sel') + self.write = self.owin.write + +#class PseudoFile: +# +# def __init__(self, owin, tags, mark="end"): +# self.owin = owin +# self.tags = tags +# self.mark = mark + +# def write(self, s): +# self.owin.write(s, self.tags, self.mark) + +# def writelines(self, l): +# map(self.write, l) + +# def flush(self): +# pass + + + + + + + diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 3e512e3..d335c7e 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -1,34 +1,10 @@ #! /usr/bin/env python -# changes by dscherer@cmu.edu - -# The main() function has been replaced by a whole class, in order to -# address the constraint that only one process can sit on the port -# hard-coded into the loader. - -# It attempts to load the RPC protocol server and publish itself. If -# that fails, it assumes that some other copy of IDLE is already running -# on the port and attempts to contact it. It then uses the RPC mechanism -# to ask that copy to do whatever it was instructed (via the command -# line) to do. (Think netscape -remote). The handling of command line -# arguments for remotes is still very incomplete. - -# Default behavior (no command line options) is to open an editor window -# instead of starting the Python Shell. However, if called as -# Pyshell.main(0), the Shell will be started instead of the editor window. - -# In the default editor mode, if files are specified, they are opened. - -# If any command line options are specified, a shell does appear, and if -# the -e option is used, both a shell and an editor window open. - import os -import spawn import sys import string import getopt import re -import protocol import socket import time import warnings @@ -44,13 +20,14 @@ from EditorWindow import EditorWindow, fixwordbreaks from FileList import FileList from ColorDelegator import ColorDelegator from UndoDelegator import UndoDelegator -from OutputWindow import OutputWindow, OnDemandOutputWindow +from OutputWindow import OutputWindow from configHandler import idleConf import idlever import rpc -use_subprocess = 0 # Set to 1 to spawn subprocess for command execution +# XX hardwire this for now, remove later KBK 09Jun02 +use_subprocess = 1 # Set to 1 to spawn subprocess for command execution # Change warnings module to write to sys.__stderr__ try: @@ -204,9 +181,6 @@ class ModifiedInterpreter(InteractiveInterpreter): InteractiveInterpreter.__init__(self, locals=locals) self.save_warnings_filters = None - global flist - self.output = OnDemandOutputWindow(flist) - rpcclt = None rpcpid = None @@ -226,14 +200,14 @@ class ModifiedInterpreter(InteractiveInterpreter): if i > 3: print >>sys.__stderr__, "Socket error:", err, "; retry..." else: - # XXX Make this a dialog? + # XXX Make this a dialog? #GvR print >>sys.__stderr__, "Can't spawn subprocess!" + # XXX Add Stephen's error msg, resolve the two later... KBK 09Jun02 + display_port_binding_error() return - self.output.stdout=PseudoFile(self.output, "stdout") - self.output.stderr=PseudoFile(self.output, "stderr") - self.rpcclt.register("stdin", self.output) - self.rpcclt.register("stdout", self.output.stdout) - self.rpcclt.register("stderr", self.output.stderr) + self.rpcclt.register("stdin", self.tkconsole) + self.rpcclt.register("stdout", self.tkconsole.stdout) + self.rpcclt.register("stderr", self.tkconsole.stderr) self.rpcclt.register("flist", self.tkconsole.flist) self.poll_subprocess() @@ -629,7 +603,7 @@ class PyShell(OutputWindow): def begin(self): self.resetoutput() - self.write("Python %s on %s\n%s\nIDLE Fork %s -- press F1 for help\n" % + self.write("Python %s on %s\n%s\nGRPC IDLE Fork %s\n" % (sys.version, sys.platform, self.COPYRIGHT, idlever.IDLE_VERSION)) try: @@ -795,8 +769,9 @@ class PyShell(OutputWindow): i = i-1 line = line[:i] more = self.interp.runsource(line) - if not more: - self.showprompt() + # XXX This was causing extra prompt with shell KBK +# if not more: +# self.showprompt() def cancel_check(self, frame, what, args, dooneevent=tkinter.dooneevent, @@ -876,6 +851,7 @@ class PseudoFile: def isatty(self): return 1 + usage_msg = """\ usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] [arg] ... @@ -886,194 +862,115 @@ idle file(s) (without options) edit the file(s) -e edit mode; arguments are files to be edited -i open an interactive shell -i file(s) open a shell and also an editor window for each file --r script use experimental remote (subprocess) execution feature +-r -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else -t title set title of shell window Remaining arguments are applied to the command (-c) or script (-r). """ -class usageError: - def __init__(self, string): self.string = string - def __repr__(self): return self.string - -class main: - def __init__(self, noshell=1): - - global flist, root - root = Tk(className="Idle") - fixwordbreaks(root) - root.withdraw() - flist = PyShellFileList(root) - - # the following causes lockups and silent failures when debugging - # changes to EditorWindow.__init__ ; the console works fine for idle - # debugging in any case, so disable this unnescesary stuff. - #dbg=OnDemandOutputWindow(flist) - #dbg.set_title('IDLE Debugging Messages') - #sys.stdout = PseudoFile(dbg,['stdout']) - #sys.stderr = PseudoFile(dbg,['stderr']) - - try: - self.server = protocol.Server(connection_hook = self.address_ok) - protocol.publish( 'IDLE', self.connect ) - self.main(sys.argv[1:], noshell) - return - except protocol.connectionLost: - try: - client = protocol.Client() - IDLE = client.getobject('IDLE') - if IDLE: - try: - IDLE.remote( sys.argv[1:] ) - except usageError, msg: - sys.stderr.write("Error: %s\n" % str(msg)) - sys.stderr.write(usage_msg) - return - except protocol.connectionLost: - pass - - #maybe the following should be handled by a tkmessagebox for - #users who don't start idle from a console?? - print """\ +def main(): + cmd = None + edit = 0 + debug = 0 + script = None + startup = 0 + + try: + opts, args = getopt.getopt(sys.argv[1:], "c:deir:st:") + except getopt.error, msg: + sys.stderr.write("Error: %s\n" % str(msg)) + sys.stderr.write(usage_msg) + sys.exit(2) + + for o, a in opts: + if o == '-c': + cmd = a + if o == '-d': + debug = 1 + if o == '-e': + edit = 1 + if o == '-r': + script = a + if o == '-s': + startup = 1 + if o == '-t': + PyShell.shell_title = a + + if args and args[0] != "-": edit = 1 + + for i in range(len(sys.path)): + sys.path[i] = os.path.abspath(sys.path[i]) + + pathx = [] + if edit: + for filename in args: + pathx.append(os.path.dirname(filename)) + elif args and args[0] != "-": + pathx.append(os.path.dirname(args[0])) + else: + pathx.append(os.curdir) + for dir in pathx: + dir = os.path.abspath(dir) + if not dir in sys.path: + sys.path.insert(0, dir) + + global flist, root + root = Tk(className="Idle") + fixwordbreaks(root) + root.withdraw() + flist = PyShellFileList(root) + + if edit: + for filename in args: + flist.open(filename) + if not args: + flist.new() + else: + if cmd: + sys.argv = ["-c"] + args + else: + sys.argv = args or [""] + + shell = PyShell(flist) + interp = shell.interp + flist.pyshell = shell + + if startup: + filename = os.environ.get("IDLESTARTUP") or \ + os.environ.get("PYTHONSTARTUP") + if filename and os.path.isfile(filename): + interp.execfile(filename) + + if debug: + shell.open_debugger() + if cmd: + interp.execsource(cmd) + elif script: + if os.path.isfile(script): + interp.execfile(script) + else: + print "No script file: ", script + shell.begin() + root.mainloop() + root.destroy() + +def display_port_binding_error(): + print """\ IDLE cannot run. -IDLE needs to use a specific TCP/IP port (7454) in order to execute and +IDLE needs to use a specific TCP/IP port (8833) in order to execute and debug programs. IDLE is unable to bind to this port, and so cannot start. Here are some possible causes of this problem: 1. TCP/IP networking is not installed or not working on this computer 2. Another program is running that uses this port - 3. Another copy of IDLE stopped responding but is still bound to the port - 4. Personal firewall software is preventing IDLE from using this port + 3. Personal firewall software is preventing IDLE from using this port IDLE makes and accepts connections only with this computer, and does not -communicate over the internet in any way. It's use of port 7454 should not +communicate over the internet in any way. Its use of port 8833 should not be a security risk on a single-user machine. """ - dbg.owin.gotoline(1) - dbg.owin.remove_selection() - root.mainloop() # wait for user to read message - - def idle(self): - spawn.kill_zombies() - self.server.rpc_loop() - root.after(25, self.idle) - - # We permit connections from localhost only - def address_ok(self, addr): - return addr[0] == '127.0.0.1' - - def connect(self, client, addr): - return self - - def remote( self, argv ): - # xxx Should make this behavior match the behavior in main, or redo - # command line options entirely. - - try: - opts, args = getopt.getopt(argv, "c:deist:") - except getopt.error, msg: - raise usageError(msg) - - for filename in args: - flist.open(filename) - if not args: - flist.new() - - def main(self, argv, noshell): - cmd = None - edit = 0 - debug = 0 - interactive = 0 - script = None - startup = 0 - global use_subprocess - - try: - opts, args = getopt.getopt(sys.argv[1:], "c:deir:st:") - except getopt.error, msg: - sys.stderr.write("Error: %s\n" % str(msg)) - sys.stderr.write(usage_msg) - sys.exit(2) - - for o, a in opts: - noshell = 0 # There are options, bring up a shell - if o == '-c': - cmd = a - if o == '-d': - debug = 1 - if o == '-e': - edit = 1 - if o == '-i': - interactive = 1 - if o == '-r': - edit = 1 - script = a - use_subprocess = 1 - if o == '-s': - startup = 1 - if o == '-t': - PyShell.shell_title = a - - if noshell: edit=1 - if interactive and args and args[0] != "-": edit = 1 - - for i in range(len(sys.path)): - sys.path[i] = os.path.abspath(sys.path[i]) - - pathx = [] - if edit: - for filename in args: - pathx.append(os.path.dirname(filename)) - elif args and args[0] != "-": - pathx.append(os.path.dirname(args[0])) - else: - pathx.append(os.curdir) - for dir in pathx: - dir = os.path.abspath(dir) - if not dir in sys.path: - sys.path.insert(0, dir) - - if edit: - for filename in args: - flist.open(filename) - if not args: - flist.new() - else: - if cmd: - sys.argv = ["-c"] + args - else: - sys.argv = args or [""] - - if noshell: - flist.pyshell = None - else: - shell = PyShell(flist) - interp = shell.interp - flist.pyshell = shell - - if startup: - filename = os.environ.get("IDLESTARTUP") or \ - os.environ.get("PYTHONSTARTUP") - if filename and os.path.isfile(filename): - interp.execfile(filename) - - if debug: - shell.open_debugger() - if cmd: - interp.execsource(cmd) - elif script: - if os.path.isfile(script): - interp.execfile(script) - else: - print "No script file: ", script - shell.begin() - - self.idle() - root.mainloop() - root.destroy() - if __name__ == "__main__": main() diff --git a/Lib/idlelib/Remote.py b/Lib/idlelib/Remote.py deleted file mode 100644 index facba78..0000000 --- a/Lib/idlelib/Remote.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Remote - This module is imported by the loader and serves to control - the execution of the user program. It presently executes files - and reports exceptions to IDLE. It could be extended to provide - other services, such as interactive mode and debugging. To that - end, it could be a subclass of e.g. InteractiveInterpreter. - - Two other classes, pseudoIn and pseudoOut, are file emulators also - used by loader. -""" -import sys, os -import traceback - -class Remote: - def __init__(self, main, master): - self.main = main - self.master = master - self.this_file = self.canonic( self.__init__.im_func.func_code.co_filename ) - - def canonic(self, path): - return os.path.normcase(os.path.abspath(path)) - - def mainloop(self): - while 1: - args = self.master.get_command() - - try: - f = getattr(self,args[0]) - apply(f,args[1:]) - except: - if not self.report_exception(): raise - - def finish(self): - sys.exit() - - def run(self, *argv): - sys.argv = argv - - path = self.canonic( argv[0] ) - dir = self.dir = os.path.dirname(path) - os.chdir(dir) - - sys.path[0] = dir - - usercode = open(path) - exec usercode in self.main - - def report_exception(self): - try: - type, value, tb = sys.exc_info() - sys.last_type = type - sys.last_value = value - sys.last_traceback = tb - - tblist = traceback.extract_tb(tb) - - # Look through the traceback, canonicalizing filenames and - # eliminating leading and trailing system modules. - first = last = 1 - for i in range(len(tblist)): - filename, lineno, name, line = tblist[i] - filename = self.canonic(filename) - tblist[i] = filename, lineno, name, line - - dir = os.path.dirname(filename) - if filename == self.this_file: - first = i+1 - elif dir==self.dir: - last = i+1 - - # Canonicalize the filename in a syntax error, too: - if type is SyntaxError: - try: - msg, (filename, lineno, offset, line) = value - filename = self.canonic(filename) - value = msg, (filename, lineno, offset, line) - except: - pass - - return self.master.program_exception( type, value, tblist, first, last ) - finally: - # avoid any circular reference through the traceback - del tb - -class pseudoIn: - def __init__(self, readline): - self.readline = readline - def isatty(): - return 1 - -class pseudoOut: - def __init__(self, func, **kw): - self.func = func - self.kw = kw - def write(self, *args): - return apply( self.func, args, self.kw ) - def writelines(self, l): - map(self.write, l) - def flush(self): - pass - diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py index 906b4f2..68f9cd7 100644 --- a/Lib/idlelib/ScriptBinding.py +++ b/Lib/idlelib/ScriptBinding.py @@ -1,17 +1,16 @@ """Extension to execute code outside the Python shell window. -This adds the following commands (to the Edit menu, until there's a -separate Python menu): +This adds the following commands: -- Check module (Alt-F5) does a full syntax check of the current module. +- Check module does a full syntax check of the current module. It also runs the tabnanny to catch any inconsistent tabs. -- Import module (F5) is equivalent to either import or reload of the +- Import module is equivalent to either import or reload of the current module. The window must have been saved previously. The module is added to sys.modules, and is also added to the __main__ namespace. Output goes to the shell window. -- Run module (Control-F5) does the same but executes the module's +- Run module does the same but executes the module's code in the __main__ namespace. XXX Redesign this interface (yet again) as follows: @@ -41,12 +40,14 @@ how many spaces a tab is worth. To fix case 2, change all tabs to spaces by using Select All followed \ by Untabify Region (both in the Edit menu).""" + +# XXX TBD Implement stop-execution KBK 11Jun02 class ScriptBinding: menudefs = [ - ('edit', [None, - ('Check module', '<>'), - ('Import module', '<>'), + ('run', [None, +# ('Check module', '<>'), +# ('Import module', '<>'), ('Run script', '<>'), ] ), diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index acdb359..bb350f2 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -42,17 +42,17 @@ enable=1 [ZoomHeight_cfgBindings] zoom-height= -[ExecBinding] -enable=1 -[ExecBinding_cfgBindings] -run-complete-script= -stop-execution= +#[ExecBinding] # Revert to ScriptBinding +#enable=1 +#[ExecBinding_cfgBindings] +#run-complete-script= +#stop-execution= -#[ScriptBinding] #currently ExecBinding has replaced ScriptBinding -#enable=0 -#[ScriptBinding_cfgBindings] -#run-script= -#check-module= +[ScriptBinding] +enable=1 +[ScriptBinding_cfgBindings] +run-script= +#check-module= #import-module= [CallTips] diff --git a/Lib/idlelib/config.txt b/Lib/idlelib/config.txt index 11adae1..b3f4ce9 100644 --- a/Lib/idlelib/config.txt +++ b/Lib/idlelib/config.txt @@ -51,9 +51,7 @@ cursor-background= black #[ZoomHeight] -#[ScriptBinding] # disabled in favor of ExecBinding - -[ExecBinding] +[ScriptBinding] [CallTips] diff --git a/Lib/idlelib/loader.py b/Lib/idlelib/loader.py deleted file mode 100644 index 6a438c3..0000000 --- a/Lib/idlelib/loader.py +++ /dev/null @@ -1,64 +0,0 @@ -# Everything is done inside the loader function so that no other names -# are placed in the global namespace. Before user code is executed, -# even this name is unbound. -def loader(): - import sys, os, protocol, threading, time - import Remote - -## Use to debug the loading process itself: -## sys.stdout = open('c:\\windows\\desktop\\stdout.txt','a') -## sys.stderr = open('c:\\windows\\desktop\\stderr.txt','a') - - # Ensure that there is absolutely no pollution of the global - # namespace by deleting the global name of this function. - global loader - del loader - - # Connect to IDLE - try: - client = protocol.Client() - except protocol.connectionLost, cL: - print 'loader: Unable to connect to IDLE', cL - return - - # Connect to an ExecBinding object that needs our help. If - # the user is starting multiple programs right now, we might get a - # different one than the one that started us. Proving that's okay is - # left as an exercise to the reader. (HINT: Twelve, by the pigeonhole - # principle) - ExecBinding = client.getobject('ExecBinding') - if not ExecBinding: - print "loader: IDLE does not need me." - return - - # All of our input and output goes through ExecBinding. - sys.stdin = Remote.pseudoIn( ExecBinding.readline ) - sys.stdout = Remote.pseudoOut( ExecBinding.write.void, tag="stdout" ) - sys.stderr = Remote.pseudoOut( ExecBinding.write.void, tag="stderr" ) - - # Create a Remote object and start it running. - remote = Remote.Remote(globals(), ExecBinding) - rthread = threading.Thread(target=remote.mainloop) - rthread.setDaemon(1) - rthread.start() - - # Block until either the client or the user program stops - user = rthread.isAlive - while user and client.isAlive(): - time.sleep(0.025) - - if not user(): - user = hasattr(sys, "ready_to_exit") and sys.ready_to_exit - for t in threading.enumerate(): - if not t.isDaemon() and t.isAlive() and t!=threading.currentThread(): - user = t.isAlive - break - - # We need to make sure we actually exit, so that the user doesn't get - # stuck with an invisible process. We want to finalize C modules, so - # we don't use os._exit(), but we don't call sys.exitfunc, which might - # block forever. - del sys.exitfunc - sys.exit() - -loader() diff --git a/Lib/idlelib/protocol.py b/Lib/idlelib/protocol.py deleted file mode 100644 index f3f6382..0000000 --- a/Lib/idlelib/protocol.py +++ /dev/null @@ -1,369 +0,0 @@ -"""protocol (David Scherer ) - - This module implements a simple RPC or "distributed object" protocol. - I am probably the 100,000th person to write this in Python, but, hey, - it was fun. - - Contents: - - connectionLost is an exception that will be thrown by functions in - the protocol module or calls to remote methods that fail because - the remote program has closed the socket or because no connection - could be established in the first place. - - Server( port=None, connection_hook=None ) creates a server on a - well-known port, to which clients can connect. When a client - connects, a Connection is created for it. If connection_hook - is defined, then connection_hook( socket.getpeername() ) is called - before a Connection is created, and if it returns false then the - connection is refused. connection_hook must be prepared to be - called from any thread. - - Client( ip='127.0.0.1', port=None ) returns a Connection to a Server - object at a well-known address and port. - - Connection( socket ) creates an RPC connection on an arbitrary socket, - which must already be connected to another program. You do not - need to use this directly if you are using Client() or Server(). - - publish( name, connect_function ) provides an object with the - specified name to some or all Connections. When another program - calls Connection.getobject() with the specified name, the - specified connect_function is called with the arguments - - connect_function( conn, addr ) - - where conn is the Connection object to the requesting client and - addr is the address returned by socket.getpeername(). If that - function returns an object, that object becomes accessible to - the caller. If it returns None, the caller's request fails. - - Connection objects: - - .close() refuses additional RPC messages from the peer, and notifies - the peer that the connection has been closed. All pending remote - method calls in either program will fail with a connectionLost - exception. Further remote method calls on this connection will - also result in errors. - - .getobject(name) returns a proxy for the remote object with the - specified name, if it exists and the peer permits us access. - Otherwise, it returns None. It may throw a connectionLost - exception. The returned proxy supports basic attribute access - and method calls, and its methods have an extra attribute, - .void, which is a function that has the same effect but always - returns None. This last capability is provided as a performance - hack: object.method.void(params) can return without waiting for - the remote process to respond, but object.method(params) needs - to wait for a return value or exception. - - .rpc_loop(block=0) processes *incoming* messages for this connection. - If block=1, it continues processing until an exception or return - value is received, which is normally forever. Otherwise it - returns when all currently pending messages have been delivered. - It may throw a connectionLost exception. - - .set_close_hook(f) specifies a function to be called when the remote - object closes the connection during a call to rpc_loop(). This - is a good way for servers to be notified when clients disconnect. - - .set_shutdown_hook(f) specifies a function called *immediately* when - the receive loop detects that the connection has been lost. The - provided function must be prepared to run in any thread. - - Server objects: - - .rpc_loop() processes incoming messages on all connections, and - returns when all pending messages have been processed. It will - *not* throw connectionLost exceptions; the - Connection.set_close_hook() mechanism is much better for servers. -""" - -import sys, os, string, types -import socket -from threading import Thread -from Queue import Queue, Empty -from cPickle import Pickler, Unpickler, PicklingError - -class connectionLost: - def __init__(self, what=""): self.what = what - def __repr__(self): return self.what - def __str__(self): return self.what - -def getmethods(cls): - "Returns a list of the names of the methods of a class." - methods = [] - for b in cls.__bases__: - methods = methods + getmethods(b) - d = cls.__dict__ - for k in d.keys(): - if type(d[k])==types.FunctionType: - methods.append(k) - return methods - -class methodproxy: - "Proxy for a method of a remote object." - def __init__(self, classp, name): - self.classp=classp - self.name=name - self.client = classp.client - def __call__(self, *args, **keywords): - return self.client.call( 'm', self.classp.name, self.name, args, keywords ) - - def void(self, *args, **keywords): - self.client.call_void( 'm', self.classp.name,self.name,args,keywords) - -class classproxy: - "Proxy for a remote object." - def __init__(self, client, name, methods): - self.__dict__['client'] = client - self.__dict__['name'] = name - - for m in methods: - prox = methodproxy( self, m ) - self.__dict__[m] = prox - - def __getattr__(self, attr): - return self.client.call( 'g', self.name, attr ) - - def __setattr__(self, attr, value): - self.client.call_void( 's', self.name, attr, value ) - -local_connect = {} -def publish(name, connect_function): - local_connect[name]=connect_function - -class socketFile: - "File emulator based on a socket. Provides only blocking semantics for now." - - def __init__(self, socket): - self.socket = socket - self.buffer = '' - - def _recv(self,bytes): - try: - r=self.socket.recv(bytes) - except: - raise connectionLost() - if not r: - raise connectionLost() - return r - - def write(self, string): - try: - self.socket.send( string ) - except: - raise connectionLost() - - def read(self,bytes): - x = bytes-len(self.buffer) - while x>0: - self.buffer=self.buffer+self._recv(x) - x = bytes-len(self.buffer) - s = self.buffer[:bytes] - self.buffer=self.buffer[bytes:] - return s - - def readline(self): - while 1: - f = string.find(self.buffer,'\n') - if f>=0: - s = self.buffer[:f+1] - self.buffer=self.buffer[f+1:] - return s - self.buffer = self.buffer + self._recv(1024) - - -class Connection (Thread): - debug = 0 - def __init__(self, socket): - self.local_objects = {} - self.socket = socket - self.name = socket.getpeername() - self.socketfile = socketFile(socket) - self.queue = Queue(-1) - self.refuse_messages = 0 - self.cmds = { 'm': self.r_meth, - 'g': self.r_get, - 's': self.r_set, - 'o': self.r_geto, - 'e': self.r_exc, - #'r' handled by rpc_loop - } - - Thread.__init__(self) - self.setDaemon(1) - self.start() - - def getobject(self, name): - methods = self.call( 'o', name ) - if methods is None: return None - return classproxy(self, name, methods) - - # close_hook is called from rpc_loop(), like a normal remote method - # invocation - def set_close_hook(self,hook): self.close_hook = hook - - # shutdown_hook is called directly from the run() thread, and needs - # to be "thread safe" - def set_shutdown_hook(self,hook): self.shutdown_hook = hook - - close_hook = None - shutdown_hook = None - - def close(self): - self._shutdown() - self.refuse_messages = 1 - - def call(self, c, *args): - self.send( (c, args, 1 ) ) - return self.rpc_loop( block = 1 ) - - def call_void(self, c, *args): - try: - self.send( (c, args, 0 ) ) - except: - pass - - # the following methods handle individual RPC calls: - - def r_geto(self, obj): - c = local_connect.get(obj) - if not c: return None - o = c(self, self.name) - if not o: return None - self.local_objects[obj] = o - return getmethods(o.__class__) - - def r_meth(self, obj, name, args, keywords): - return apply( getattr(self.local_objects[obj],name), args, keywords) - - def r_get(self, obj, name): - return getattr(self.local_objects[obj],name) - - def r_set(self, obj, name, value): - setattr(self.local_objects[obj],name,value) - - def r_exc(self, e, v): - raise e, v - - def rpc_exec(self, cmd, arg, ret): - if self.refuse_messages: return - if self.debug: print cmd,arg,ret - if ret: - try: - r=apply(self.cmds.get(cmd), arg) - self.send( ('r', r, 0) ) - except: - try: - self.send( ('e', sys.exc_info()[:2], 0) ) - except PicklingError: - self.send( ('e', (TypeError, 'Unpicklable exception.'), 0 ) ) - else: - # we cannot report exceptions to the caller, so - # we report them in this process. - r=apply(self.cmds.get(cmd), arg) - - # the following methods implement the RPC and message loops: - - def rpc_loop(self, block=0): - if self.refuse_messages: raise connectionLost('(already closed)') - try: - while 1: - try: - cmd, arg, ret = self.queue.get( block ) - except Empty: - return None - if cmd=='r': return arg - self.rpc_exec(cmd,arg,ret) - except connectionLost: - if self.close_hook: - self.close_hook() - self.close_hook = None - raise - - def run(self): - try: - while 1: - data = self.recv() - self.queue.put( data ) - except: - self.queue.put( ('e', sys.exc_info()[:2], 0) ) - - # The following send raw pickled data to the peer - - def send(self, data): - try: - Pickler(self.socketfile,1).dump( data ) - except connectionLost: - self._shutdown() - if self.shutdown_hook: self.shutdown_hook() - raise - - def recv(self): - try: - return Unpickler(self.socketfile).load() - except connectionLost: - self._shutdown() - if self.shutdown_hook: self.shutdown_hook() - raise - except: - raise - - def _shutdown(self): - try: - self.socket.shutdown(1) - self.socket.close() - except: - pass - - -class Server (Thread): - default_port = 0x1D1E # "IDlE" - - def __init__(self, port=None, connection_hook=None): - self.connections = [] - self.port = port or self.default_port - self.connection_hook = connection_hook - - try: - self.wellknown = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(('', self.port)) - s.listen(3) - except: - raise connectionLost - - Thread.__init__(self) - self.setDaemon(1) - self.start() - - def run(self): - s = self.wellknown - while 1: - conn, addr = s.accept() - if self.connection_hook and not self.connection_hook(addr): - try: - conn.shutdown(1) - except: - pass - continue - self.connections.append( Connection(conn) ) - - def rpc_loop(self): - cns = self.connections[:] - for c in cns: - try: - c.rpc_loop(block = 0) - except connectionLost: - if c in self.connections: - self.connections.remove(c) - -def Client(ip='127.0.0.1', port=None): - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((ip,port or Server.default_port)) - except socket.error, what: - raise connectionLost(str(what)) - except: - raise connectionLost() - return Connection(s) diff --git a/Lib/idlelib/spawn.py b/Lib/idlelib/spawn.py deleted file mode 100644 index be8fdf7..0000000 --- a/Lib/idlelib/spawn.py +++ /dev/null @@ -1,58 +0,0 @@ -# spawn - This is ugly, OS-specific code to spawn a separate process. It -# also defines a function for getting the version of a path most -# likely to work with cranky API functions. - -import os - -def hardpath(path): - path = os.path.normcase(os.path.abspath(path)) - try: - import win32api - path = win32api.GetShortPathName( path ) - except: - pass - return path - -if hasattr(os, 'fork'): - - # UNIX-ish operating system: we fork() and exec(), and we have to track - # the pids of our children and call waitpid() on them to avoid leaving - # zombies in the process table. kill_zombies() does the dirty work, and - # should be called periodically. - - zombies = [] - - def spawn(bin, *args): - pid = os.fork() - if pid: - zombies.append(pid) - else: - os.execv( bin, (bin, ) + args ) - - def kill_zombies(): - for z in zombies[:]: - stat = os.waitpid(z, os.WNOHANG) - if stat[0]==z: - zombies.remove(z) -elif hasattr(os, 'spawnv'): - - # Windows-ish OS: we use spawnv(), and stick quotes around arguments - # in case they contains spaces, since Windows will jam all the - # arguments to spawn() or exec() together into one string. The - # kill_zombies function is a noop. - - def spawn(bin, *args): - nargs = ['"'+bin+'"'] - for arg in args: - nargs.append( '"'+arg+'"' ) - os.spawnv( os.P_NOWAIT, bin, nargs ) - - def kill_zombies(): pass - -else: - # If you get here, you may be able to write an alternative implementation - # of these functions for your OS. - - def kill_zombies(): pass - - raise OSError, 'This OS does not support fork() or spawnv().' -- cgit v0.12