Rework the code to have only the GvR RPC. Output from execution of user
code is directed to the Shell.
This commit is contained in:
parent
9f709bf9a1
commit
969de458aa
|
@ -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
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
"""Extension to execute a script in a separate process
|
||||
|
||||
David Scherer <dscherer@cmu.edu>
|
||||
|
||||
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', '<<run-complete-script>>'),
|
||||
('Stop program', '<<stop-execution>>'),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
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
|
|
@ -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 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)
|
||||
|
||||
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.
|
||||
|
||||
"""An editor window that can serve as an output file.
|
||||
|
||||
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("<<goto-file-line>>", self.goto_file_line)
|
||||
self.text.bind("<<newline-and-indent>>", 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
def main():
|
||||
cmd = None
|
||||
edit = 0
|
||||
debug = 0
|
||||
script = None
|
||||
startup = 0
|
||||
|
||||
class main:
|
||||
def __init__(self, noshell=1):
|
||||
|
||||
global flist, root
|
||||
root = Tk(className="Idle")
|
||||
fixwordbreaks(root)
|
||||
root.withdraw()
|
||||
flist = PyShellFileList(root)
|
||||
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)
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
#maybe the following should be handled by a tkmessagebox for
|
||||
#users who don't start idle from a console??
|
||||
print """\
|
||||
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()
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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', '<<check-module>>'),
|
||||
('Import module', '<<import-module>>'),
|
||||
('run', [None,
|
||||
# ('Check module', '<<check-module>>'),
|
||||
# ('Import module', '<<import-module>>'),
|
||||
('Run script', '<<run-script>>'),
|
||||
]
|
||||
),
|
||||
|
|
|
@ -42,17 +42,17 @@ enable=1
|
|||
[ZoomHeight_cfgBindings]
|
||||
zoom-height=<Alt-Key-F2>
|
||||
|
||||
[ExecBinding]
|
||||
enable=1
|
||||
[ExecBinding_cfgBindings]
|
||||
run-complete-script=<Key-F5>
|
||||
stop-execution=<Key-Cancel>
|
||||
#[ExecBinding] # Revert to ScriptBinding
|
||||
#enable=1
|
||||
#[ExecBinding_cfgBindings]
|
||||
#run-complete-script=<Key-F5>
|
||||
#stop-execution=<Key-Cancel>
|
||||
|
||||
#[ScriptBinding] #currently ExecBinding has replaced ScriptBinding
|
||||
#enable=0
|
||||
#[ScriptBinding_cfgBindings]
|
||||
#run-script=<Key-F5>
|
||||
#check-module=<Alt-Key-F5> <Meta-Key-F5>
|
||||
[ScriptBinding]
|
||||
enable=1
|
||||
[ScriptBinding_cfgBindings]
|
||||
run-script=<Key-F5>
|
||||
#check-module=<Alt-Key-F5>
|
||||
#import-module=<Control-Key-F5>
|
||||
|
||||
[CallTips]
|
||||
|
|
|
@ -51,9 +51,7 @@ cursor-background= black
|
|||
|
||||
#[ZoomHeight]
|
||||
|
||||
#[ScriptBinding] # disabled in favor of ExecBinding
|
||||
|
||||
[ExecBinding]
|
||||
[ScriptBinding]
|
||||
|
||||
[CallTips]
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -1,369 +0,0 @@
|
|||
"""protocol (David Scherer <dscherer@cmu.edu>)
|
||||
|
||||
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)
|
|
@ -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().'
|
Loading…
Reference in New Issue