[svn r80] DEV-9309: Support python object copy through saranwrap. Also tweaked comment on event.send to better reflect actual semantics.

This commit is contained in:
which.linden
2008-01-23 19:14:38 -05:00
parent da1b614455
commit 9ec1cab842
3 changed files with 65 additions and 36 deletions

View File

@@ -83,8 +83,8 @@ class event(object):
0, greenlib.switch, waiter, None, Cancelled()) 0, greenlib.switch, waiter, None, Cancelled())
def send(self, result=None, exc=None): def send(self, result=None, exc=None):
"""Resume all previous and further """Makes arrangements for the waiters to be woken with the
calls to wait() with result. result and then returns immediately to the parent.
""" """
assert self._result is NOT_USED assert self._result is NOT_USED
self._result = result self._result = result

View File

@@ -397,6 +397,19 @@ not need to deal with this class directly."""
# see description for __repr__, len(obj) is the same. # see description for __repr__, len(obj) is the same.
return self.__len__() return self.__len__()
def __deepcopy__(self, memo=None):
"""Copies the entire external object and returns its
value. Will only work if the remote object is pickleable."""
my_cp = self.__local_dict['_cp']
my_id = self.__local_dict['_id']
request = Request('copy', {'id':my_id})
return my_cp.make_request(request)
# since the remote object is being serialized whole anyway,
# there's no semantic difference between copy and deepcopy
__copy__ = __deepcopy__
def proxied_type(self): def proxied_type(self):
if type(self) is not ObjectProxy: if type(self) is not ObjectProxy:
return type(self) return type(self)
@@ -444,60 +457,60 @@ when the id is None."""
self._next_id = 1 self._next_id = 1
self._objects = {} self._objects = {}
def handle_status(self, object, req): def handle_status(self, obj, req):
return { return {
'object_count':len(self._objects), 'object_count':len(self._objects),
'next_id':self._next_id, 'next_id':self._next_id,
'pid':os.getpid()} 'pid':os.getpid()}
def handle_getattr(self, object, req): def handle_getattr(self, obj, req):
try: try:
return getattr(object, req['attribute']) return getattr(obj, req['attribute'])
except AttributeError, e: except AttributeError, e:
if hasattr(object, "__getitem__"): if hasattr(obj, "__getitem__"):
return object[req['attribute']] return obj[req['attribute']]
else: else:
raise e raise e
#_log('getattr: %s' % str(response)) #_log('getattr: %s' % str(response))
def handle_setattr(self, object, req): def handle_setattr(self, obj, req):
try: try:
return setattr(object, req['attribute'], req['value']) return setattr(obj, req['attribute'], req['value'])
except AttributeError, e: except AttributeError, e:
if hasattr(object, "__setitem__"): if hasattr(obj, "__setitem__"):
return object.__setitem__(req['attribute'], req['value']) return obj.__setitem__(req['attribute'], req['value'])
else: else:
raise e raise e
def handle_getitem(self, object, req): def handle_getitem(self, obj, req):
return object[req['key']] return obj[req['key']]
def handle_setitem(self, object, req): def handle_setitem(self, obj, req):
object[req['key']] = req['value'] obj[req['key']] = req['value']
return None # *TODO figure out what the actual return value of __setitem__ should be return None # *TODO figure out what the actual return value of __setitem__ should be
def handle_eq(self, object, req): def handle_eq(self, obj, req):
#_log("__eq__ %s %s" % (object, req)) #_log("__eq__ %s %s" % (obj, req))
rhs = None rhs = None
try: try:
rhs = self._objects[req['rhs']] rhs = self._objects[req['rhs']]
except KeyError, e: except KeyError, e:
return False return False
return (object == rhs) return (obj == rhs)
def handle_call(self, object, req): def handle_call(self, obj, req):
#_log("calling %s " % (req['name'])) #_log("calling %s " % (req['name']))
try: try:
fn = getattr(object, req['name']) fn = getattr(obj, req['name'])
except AttributeError, e: except AttributeError, e:
if hasattr(object, "__setitem__"): if hasattr(obj, "__setitem__"):
fn = object[req['name']] fn = obj[req['name']]
else: else:
raise e raise e
return fn(*req['args'],**req['kwargs']) return fn(*req['args'],**req['kwargs'])
def handle_del(self, object, req): def handle_del(self, obj, req):
id = req['id'] id = req['id']
_log("del %s from %s" % (id, self._objects)) _log("del %s from %s" % (id, self._objects))
@@ -505,11 +518,14 @@ when the id is None."""
del self._objects[id] del self._objects[id]
return None return None
def handle_type(self, object, req): def handle_type(self, obj, req):
return type(object) return type(obj)
def handle_nonzero(self, object, req): def handle_nonzero(self, obj, req):
return bool(object) return bool(obj)
def handle_copy(self, obj, req):
return obj
def loop(self): def loop(self):
"""@brief Loop forever and respond to all requests.""" """@brief Loop forever and respond to all requests."""
@@ -524,20 +540,20 @@ when the id is None."""
_log("request: %s (%s)" % (request, self._objects)) _log("request: %s (%s)" % (request, self._objects))
req = request req = request
id = None id = None
object = None obj = None
try: try:
id = req['id'] id = req['id']
if id: if id:
id = int(id) id = int(id)
object = self._objects[id] obj = self._objects[id]
#_log("id, object: %d %s" % (id, object)) #_log("id, object: %d %s" % (id, obj))
except Exception, e: except Exception, e:
#_log("Exception %s" % str(e)) #_log("Exception %s" % str(e))
pass pass
if object is None or id is None: if obj is None or id is None:
id = None id = None
object = self._export obj = self._export
#_log("found object %s" % str(object)) #_log("found object %s" % str(obj))
# Handle the request via a method with a special name on the server # Handle the request via a method with a special name on the server
handler_name = 'handle_%s' % request.action() handler_name = 'handle_%s' % request.action()
@@ -547,11 +563,11 @@ when the id is None."""
except AttributeError: except AttributeError:
raise BadRequest, request.action() raise BadRequest, request.action()
response = handler(object, request) response = handler(obj, request)
# figure out what to do with the response, and respond # figure out what to do with the response, and respond
# apprpriately. # apprpriately.
if request.action() in ['status', 'type']: if request.action() in ['status', 'type', 'copy']:
# have to handle these specially since we want to # have to handle these specially since we want to
# pickle up the actual value and not return a proxy # pickle up the actual value and not return a proxy
self.respond(['value', response]) self.respond(['value', response])
@@ -627,7 +643,7 @@ def main():
description="Simple saranwrap.Server wrapper") description="Simple saranwrap.Server wrapper")
parser.add_option( parser.add_option(
'-c', '--child', default=False, action='store_true', '-c', '--child', default=False, action='store_true',
help='Wrap an object serialed via setattr.') help='Wrap an object serialized via setattr.')
parser.add_option( parser.add_option(
'-m', '--module', type='string', dest='module', default=None, '-m', '--module', type='string', dest='module', default=None,
help='a module to load and export.') help='a module to load and export.')

View File

@@ -281,6 +281,19 @@ sys_path = sys.path""")
for waiter in waiters: for waiter in waiters:
waiter.wait() waiter.wait()
def test_copy(self):
import copy
compound_object = {'a':[1,2,3]}
prox = saranwrap.wrap(compound_object)
def make_assertions(copied):
self.assert_(isinstance(copied, dict))
self.assert_(isinstance(copied['a'], list))
self.assertEquals(copied, compound_object)
self.assertNotEqual(id(compound_object), id(copied))
make_assertions(copy.copy(prox))
make_assertions(copy.deepcopy(prox))
def test_list_of_functions(self): def test_list_of_functions(self):
return # this test is known to fail, we can implement it sometime in the future if we wish return # this test is known to fail, we can implement it sometime in the future if we wish
from eventlet import saranwrap_test from eventlet import saranwrap_test