summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/protocol.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/idlelib/protocol.py')
-rw-r--r--Lib/idlelib/protocol.py369
1 files changed, 0 insertions, 369 deletions
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 <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)