git » pymisc » commit 7f26f6a

pickle_rpc: Allow registration of objects

author Alberto Bertogli
2008-12-13 21:39:37 UTC
committer Alberto Bertogli
2008-12-13 21:44:49 UTC
parent c2ba2eacf68af65375e32a3e47cfa80ab52e8c51

pickle_rpc: Allow registration of objects

This patch introduces the possibility of registering an object under a
given name, and then calling its methods remotely.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>

pickle_rpc.py +49 -7
samples/pickle_rpc/c1.py +2 -0
samples/pickle_rpc/s1.py +14 -0

diff --git a/pickle_rpc.py b/pickle_rpc.py
index 1709183..f1c4ea6 100644
--- a/pickle_rpc.py
+++ b/pickle_rpc.py
@@ -84,12 +84,35 @@ class Server (object):
 				socket.SO_REUSEADDR, 1)
 		self.fds = []
 		self.functions = {}
+		self.objects = {}
 
 	def register(self, func, name = None):
 		if not name:
 			name = func.__name__
 		self.functions[name] = func
 
+	def register_object(self, name, obj):
+		self.objects[name] = obj
+
+	def find_method(self, chain):
+		chain = chain.split('.')
+		chain, fname = chain[:-1], chain[-1]
+
+		# the first object is a special case because it comes from
+		# self.objects
+		if chain[0] not in self.objects:
+			return None
+		obj = self.objects[chain[0]]
+
+		# walk through the object names
+		for oname in chain[1:]:
+			obj = getattr(obj, oname, None)
+			if obj is None:
+				return None
+
+		# finally, return the function (if there is one)
+		return getattr(obj, fname, None)
+
 	def loop(self):
 		self.sock.listen(15)
 		while True:
@@ -132,12 +155,20 @@ class Server (object):
 		funcname = req[0]
 		params = req[1]
 		kwparams = req[2]
-		if funcname not in self.functions:
+
+		if '.' in funcname:
+			# it's a method from (hopefully) one of our registered
+			# objects
+			func = self.find_method(funcname)
+		else:
+			func = self.functions.get(funcname, None)
+
+		if func is None:
 			self.send(fd, (replies.UNKNOWN,))
 			return
 
 		try:
-			r = self.functions[funcname](*params, **kwparams)
+			r = func(*params, **kwparams)
 		except:
 			e, i, tb = sys.exc_info()
 			strtb = traceback.format_exc()
@@ -160,6 +191,20 @@ class Server (object):
 class UnknownFunction (Exception):
 	pass
 
+
+class _ImObject (object):
+	"Intermediate object returned by Remote.__getattr__()"
+	def __init__(self, remote, name):
+		self.__remote = remote
+		self.__name = name
+
+	def __call__(self, *args, **kwargs):
+		return self.__remote._rpc(self.__name, args, kwargs)
+
+	def __getattr__(self, name):
+		return _ImObject(self.__remote, self.__name + '.' + name)
+
+
 class Remote (object):
 	"Pickle-RPC client"
 	def __init__(self, ip, port = default_port):
@@ -174,12 +219,9 @@ class Remote (object):
 		return self.__last_tb_str
 
 	def __getattr__(self, name):
-		def stub_func(*args, **kwargs):
-			return self.__rpc(name, args, kwargs)
-
-		return stub_func
+		return _ImObject(self, name)
 
-	def __rpc(self, name, args, kwargs):
+	def _rpc(self, name, args, kwargs):
 		msg = (name, args, kwargs)
 		pickle.dump(msg, self.__fd, -1)
 		rep = pickle.load(self.__fd)
diff --git a/samples/pickle_rpc/c1.py b/samples/pickle_rpc/c1.py
index b7dc821..d03f002 100644
--- a/samples/pickle_rpc/c1.py
+++ b/samples/pickle_rpc/c1.py
@@ -10,6 +10,8 @@ try:
 except KeyError, info:
 	print 'successfuly caught', KeyError, info
 
+print cli.test2_i.t1.method("hi!", 1, 2, 3)
+
 # the following causes an error unpickling on the server side, and it's useful
 # for testing that code path
 #class E(Exception): pass
diff --git a/samples/pickle_rpc/s1.py b/samples/pickle_rpc/s1.py
index c703071..25ffc38 100644
--- a/samples/pickle_rpc/s1.py
+++ b/samples/pickle_rpc/s1.py
@@ -14,9 +14,23 @@ def long(n):
 		l.append((i, i + 1, i + 2, i + 3))
 	return l
 
+
+class Test1:
+	def __init__(self, n):
+		self.n = n
+
+	def method(self, *args):
+		return (self.n, args)
+
+class Test2:
+	def __init__(self, t1):
+		self.t1 = t1
+
+
 srv = pickle_rpc.Server('localhost')
 srv.register(f)
 srv.register(exc)
 srv.register(long)
+srv.register_object("test2_i", Test2(Test1("inst1")))
 srv.loop()