Allow MakeDist to use a specified sf.net account
[rox-lib.git] / python / rox / master_proxy.py
blob983d800e2243d5ba50355df12e0f1c406eb3607f
1 """This module allows a caller to invoke methods on another process.
2 It is really part of the proxy module, but separate because it imports some GTK
3 functions which a slave must not do.
5 EXPERIMENTAL.
6 """
8 from __future__ import generators
9 from proxy import Proxy
10 import tasks # (imports rox, and thus gtk)
12 class MasterObject(object):
13 """Invoking a method on a MasterObject invokes the corresponding
14 method on the slave object. The return value is a ResponseBlocker from
15 which the response can be read."""
16 _serial = 0
18 def __init__(self, master):
19 self._master = master
21 def __getattr__(self, name):
22 def method(*args):
23 self._serial += 1
24 request = self._master._add_blocker(self._serial)
25 self._master.write_object((self._serial, name, args))
26 return request
27 return method
29 def finish_proxy(self):
30 """Calls MasterProxy.finish() for our MasterProxy"""
31 self._master.finish()
33 class RequestBlocker(tasks.Blocker):
34 """The blocker is triggered when the slave object sends a reply
35 to our method call. You can then call get() to get the result, eg:
37 blocker = master.method()
38 yield blocker
39 print blocker.result
41 If the remote method raised an exception, accessing 'isresult' will raise
42 it rather than returning it.
43 """
45 def _error(self):
46 if self.error is not None:
47 raise self.error
48 raise Exception('No result yet! Yield this blocker first.')
50 master = None
51 serial = None
52 error = None
53 result = property(_error)
55 def __init__(self, master, serial):
56 tasks.Blocker.__init__(self)
57 self.master = master
58 self.serial = serial
60 def add(self, data):
61 """Store the result and trigger our blocker."""
62 assert not self.happened
63 self.master._remove_blocker(self.serial)
64 if isinstance(data, Exception):
65 self.error = data
66 else:
67 self.result = data
68 self.trigger()
70 class LostConnection(Exception):
71 pass
73 class MasterProxy(Proxy):
74 """Invoking operations on MasterProxy.root will invoke the same
75 operation on the SlaveProxy's slave_object."""
77 def __init__(self, to_slave, from_slave):
78 Proxy.__init__(self, to_slave, from_slave)
79 self.root = MasterObject(self)
80 self._queue = {} # Serial -> RequestBlocker
82 def _dispatch(self, value):
83 serial, data = value
84 self._queue[serial].add(data)
86 def _add_blocker(self, serial):
87 assert serial not in self._queue
88 request = RequestBlocker(self, serial)
89 self._queue[serial] = request
90 return request
92 def _remove_blocker(self, serial):
93 del self._queue[serial]
95 def lost_connection(self):
96 for x in self._queue.values():
97 x.add(LostConnection('Lost connection to su proxy'))
98 assert not self._queue