If a service property exists but the window it points to doesn't, then
[rox-lib.git] / python / rox / proxy.py
blob423137eab5d845915ca8de745579e2dc18e35673
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, pwd
13 import traceback
14 import fcntl
15 from select import select
16 import cPickle as pickle
18 class Proxy:
19 def __init__(self, to_peer, from_peer, slave_object = None):
20 if not hasattr(to_peer, 'fileno'):
21 to_peer = os.fdopen(to_peer, 'w')
22 if not hasattr(from_peer, 'fileno'):
23 from_peer = os.fdopen(from_peer, 'r')
24 self.to_peer = to_peer
25 self.from_peer = from_peer
26 self.out_buffer = ""
27 self.in_buffer = ""
29 self.enable_read_watch()
31 def enable_read_watch(self):
32 from rox import g
33 g.input_add(self.from_peer, g.gdk.INPUT_READ,
34 lambda src, cond: self.read_ready())
36 def enable_write_watch(self):
37 from rox import g
38 INPUT_WRITE = 0x14 # g.gdk.INPUT_WRITE sometimes wrong!!
39 g.input_add(self.to_peer.fileno(), INPUT_WRITE,
40 lambda src, cond: self.write_ready())
42 def write_object(self, object):
43 if self.to_peer is None:
44 raise Exception('Peer is defunct')
45 if not self.out_buffer:
46 self.enable_write_watch()
48 s = pickle.dumps(object)
49 s = str(len(s)) + ":" + s
50 self.out_buffer += s
52 def write_ready(self):
53 """Returns True if the buffer is not empty on exit."""
54 while self.out_buffer:
55 w = select([], [self.to_peer], [], 0)[1]
56 if not w:
57 print "Not ready for writing"
58 return True
59 n = os.write(self.to_peer.fileno(), self.out_buffer)
60 self.out_buffer = self.out_buffer[n:]
61 return False
63 def read_ready(self):
64 new = os.read(self.from_peer.fileno(), 1000)
65 if not new:
66 self.finish()
67 self.lost_connection()
68 return False
69 self.in_buffer += new
70 while ':' in self.in_buffer:
71 l, rest = self.in_buffer.split(':', 1)
72 l = int(l)
73 if len(rest) < l:
74 return True # Haven't got everything yet
75 s = rest[:l]
76 self.in_buffer = rest[l:]
77 value = pickle.loads(s)
78 self._dispatch(value)
79 return True
81 def finish(self):
82 self.to_slave = self.from_slave = None
84 def lost_connection(self):
85 raise Exception("Lost connection to peer!")
87 class SlaveProxy(Proxy):
88 """Methods invoked on MasterProxy.root will be invoked on
89 slave_object. The result is a master_proxy.RequestBlocker."""
90 def __init__(self, to_master, from_master, slave_object):
91 Proxy.__init__(self, to_master, from_master)
92 self.slave_object = slave_object
94 def _dispatch(self, value):
95 serial, method, args = value
96 try:
97 result = getattr(self.slave_object, method)(*args)
98 except Exception, e:
99 result = e
100 self.write_object((serial, result))
102 def lost_connection(self):
103 sys.exit()