Moved into a sub-dir, so that a svn checkout has the same structure as
[rox-lib/lack.git] / ROX-Lib2 / python / rox / proxy.py
blobeaf3def682d4df4e7e918188688693f36224f90f
1 """Given a pair of pipes with a python process at each end, this module
2 allows one end to make calls on the other. This is used by the su module
3 to allow control of a subprocess running as another user, but it may also
4 be useful in other situations. The caller end should use the master_proxy
5 module.
7 EXPERIMENTAL.
8 """
10 from __future__ import generators
11 # Note: do not import rox or gtk. Needs to work without DISPLAY.
12 import os, sys
13 from select import select
14 import cPickle as pickle
15 import gobject
17 class Proxy:
18 def __init__(self, to_peer, from_peer, slave_object = None):
19 if not hasattr(to_peer, 'fileno'):
20 to_peer = os.fdopen(to_peer, 'w')
21 if not hasattr(from_peer, 'fileno'):
22 from_peer = os.fdopen(from_peer, 'r')
23 self.to_peer = to_peer
24 self.from_peer = from_peer
25 self.out_buffer = ""
26 self.in_buffer = ""
28 self.enable_read_watch()
30 def enable_read_watch(self):
31 gobject.io_add_watch(self.from_peer, gobject.IO_IN,
32 lambda src, cond: self.read_ready())
34 def enable_write_watch(self):
35 gobject.io_add_watch(self.to_peer.fileno(), gobject.IO_OUT,
36 lambda src, cond: self.write_ready())
38 def write_object(self, object):
39 if self.to_peer is None:
40 raise Exception('Peer is defunct')
41 if not self.out_buffer:
42 self.enable_write_watch()
44 s = pickle.dumps(object)
45 s = str(len(s)) + ":" + s
46 self.out_buffer += s
48 def write_ready(self):
49 """Returns True if the buffer is not empty on exit."""
50 while self.out_buffer:
51 w = select([], [self.to_peer], [], 0)[1]
52 if not w:
53 print "Not ready for writing"
54 return True
55 n = os.write(self.to_peer.fileno(), self.out_buffer)
56 self.out_buffer = self.out_buffer[n:]
57 return False
59 def read_ready(self):
60 new = os.read(self.from_peer.fileno(), 1000)
61 if not new:
62 self.finish()
63 self.lost_connection()
64 return False
65 self.in_buffer += new
66 while ':' in self.in_buffer:
67 l, rest = self.in_buffer.split(':', 1)
68 l = int(l)
69 if len(rest) < l:
70 return True # Haven't got everything yet
71 s = rest[:l]
72 self.in_buffer = rest[l:]
73 value = pickle.loads(s)
74 self._dispatch(value)
75 return True
77 def finish(self):
78 self.to_slave = self.from_slave = None
80 def lost_connection(self):
81 raise Exception("Lost connection to peer!")
83 class SlaveProxy(Proxy):
84 """Methods invoked on MasterProxy.root will be invoked on
85 slave_object. The result is a master_proxy.RequestBlocker."""
86 def __init__(self, to_master, from_master, slave_object):
87 Proxy.__init__(self, to_master, from_master)
88 self.slave_object = slave_object
90 def _dispatch(self, value):
91 serial, method, args = value
92 try:
93 result = getattr(self.slave_object, method)(*args)
94 except Exception, e:
95 result = e
96 self.write_object((serial, result))
98 def lost_connection(self):
99 sys.exit()