Commits

Jesper Nøhr  committed 942530d

remote exceptions, namespace crawling, etc.

  • Participants
  • Parent commits 489b9cf

Comments (0)

Files changed (4)

File puck/client.py

 #!/usr/bin/python
 import os, sys, simplejson, socket
-from puck.core import decode, encode, ServerTimeoutException, ServerHiccup
+from puck.core import decode, encode, ServerTimeoutException, ServerHiccup, EndPointException
 
 class PuckProxy(object):
-    def __init__(self, sock, payload):
-        if payload == "":
-            raise ServerHiccup("No data returned.")
-
-        dso = simplejson.loads(payload)
-
-        if dso.get('reply', 'fail.') != 'ok.':
-            raise ServerHiccup("Fail in response.")
-
+    def __init__(self, sock, dso):
         self.raw = False
         self.dso = dso
         self.sock = sock
         return PuckShallowMethod(self.sock, attr, id_=self.id)
 
     # TODO: __setattr__
-
+    # TODO: __index-thingie__ ([0])
+    
     def __repr__(self):
         if self.raw:
             return self.repr
     def default(self, obj):
         if isinstance(obj, PuckProxy):
             return obj.dso
-        elif isinstance(obj, basestring):
-            print "encode", obj
         return simplejson.JSONEncoder.default(self, obj)    
     
 class JSONCall(simplejson.JSONEncoder):
     def __call__(self, *args, **kwargs):
         packet = JSONCall(module=self.method, id_=self.id, args=args, kwargs=kwargs)
         self.sock.sendall(packet.to_json()+'\r\n')
+
         f = self.sock.makefile('rb')
-        return PuckProxy(self.sock, f.readline()).to_native()
+        payload = f.readline()
+
+        if payload == "":
+            raise ServerHiccup("No data returned.")
+
+        dso = simplejson.loads(payload)
+
+        if dso.get('reply', 'fail.') == 'fail.':
+            raise ServerHiccup("Fail in response.")
+        elif dso.get('reply', 'fail.') == 'exception.':
+            raise EndPointException.from_json(dso)
+
+        if isinstance(dso.get('objects'), (list, tuple)):
+            return [ PuckProxy(self.sock, d).to_native() for d in dso.get('objects') ]
+        return PuckProxy(self.sock, dso.get('objects')).to_native()
 
 class PuckClient(object):
     def __init__(self, host, port):

File puck/core.py

+import simplejson, exceptions
+
 VERSION = "0.1"
 
 class ServerTimeoutException(Exception):
 class ServerHiccup(Exception):
     pass
     
+class EndPointException(Exception):
+    def to_json(self):
+        kwd = { "reply": "exception." }
+
+        kwd.update({'name': self.args[0].__class__.__name__})
+        kwd.update({'args': self.args[0].args})
+
+        print "delivering exception", kwd
+        return simplejson.dumps(kwd)
+        
+    @classmethod
+    def from_json(cls, dso):
+        name = dso.get('name')
+        args = dso.get('args')
+        
+        if hasattr(exceptions, name):
+            ex = getattr(exceptions, name)(*args)
+        
+        return ex
+    
 def encode(obj):
     if isinstance(obj, str):
         try:
 import simplejson, types
 from twisted.internet.protocol import Factory, Protocol
 from twisted.protocols.basic import LineOnlyReceiver
-from puck.core import encode, decode
+from puck.core import encode, decode, EndPointException
 
 class JSONMarshaller(object):
-    RAW_TYPES = [ types.StringType, types.BooleanType, types.DictType,
-                  types.DictionaryType, types.FloatType, types.IntType,
-                  types.ListType, types.LongType, types.NoneType,
-                  types.TupleType, types.UnicodeType ]
+    RAW_TYPES = [ types.StringType, types.BooleanType,
+                  types.FloatType, types.IntType,
+                  types.LongType, types.NoneType,
+                  types.UnicodeType, types.DictType ]
 
     def __init__(self, obj):
         self.obj = obj
                 return True
         return False
         
-    def to_json(self):
-        kwd = { 'reply': 'ok.' }
+    def to_dict(self):
+        kwd = { }
 
         if self.is_raw(self.obj):
             kwd.update({ 'data': encode(self.obj) })
         kwd.update({'id': id(self.obj)})
         kwd.update({'repr': repr(encode(self.obj))})
         
-        return simplejson.dumps(kwd, skipkeys=True)
+        return kwd
+        
+    def to_json(self):        
+        if isinstance(self.obj, (list, tuple)):
+            objs = [ JSONMarshaller(o).to_dict() for o in self.obj ]
+            obj = { 'reply': 'ok.', 'objects': objs }
+        else:
+            obj = { 'reply': 'ok.', 'objects': self.to_dict() }
+
+        print "sending", simplejson.dumps(obj)
+
+        return simplejson.dumps(obj, skipkeys=True)
 
 class Hub(LineOnlyReceiver):
     REPLY_FAIL = simplejson.dumps({"reply":"fail."})
         self.respond(reply.to_json())
         
     def lineReceived(self, line):
-        print line
         try:
             dso = simplejson.loads(line)
         except ValueError:
             self.respond(Hub.REPLY_FAIL)
             return
 
-        if dso.has_key('id') and dso.get('id', None) is not None:
-            stack_ptr = self.stack.get(dso.get('id'))
-            namespaces = dso.get('method').split(".")
+        try:
+            if dso.has_key('id') and dso.get('id', None) is not None:
+                stack_ptr = self.stack.get(dso.get('id'))
+                namespaces = dso.get('method').split(".")
+
+                if '__unicode__' in namespaces and not \
+                    hasattr(stack_ptr, '__unicode__'):
+                    cmd = lambda *a, **kwa: unicode(stack_ptr)
+                else:
+                    for ns in namespaces:
+                        stack_ptr = getattr(stack_ptr, ns)
+                
+                    cmd = stack_ptr
+            else:
+                cmd = self.find_command(dso.get('method'))
             
-            for ns in namespaces:
-                stack_ptr = getattr(stack_ptr, ns)
-            
-            cmd = stack_ptr
-        else:
-            cmd = self.find_command(dso.get('method'))
-        
-        args = decode(dso.get('args'))
-        kwargs = decode(dso.get('kwargs'))
+            args = decode(dso.get('args'))
+            kwargs = decode(dso.get('kwargs'))
+                    
+            if cmd is not None:
+                ret = cmd(*args, **kwargs)
                 
-        if cmd is not None:
-            ret = cmd(*args, **kwargs)
-            id_ = id(ret)
-            self.stack[id_] = ret
-            reply = JSONMarshaller(ret)
-            self.respond(reply.to_json())
-        else:
-            self.respond(Hub.REPLY_FAIL)
+                if isinstance(ret, (list, tuple)):
+                    for r in ret:
+                        id_ = id(r)
+                        self.stack[id_] = r
+                else:
+                    id_ = id(ret)
+                    self.stack[id_] = ret
+                    
+                reply = JSONMarshaller(ret)
+                self.respond(reply.to_json())
+            else:
+                self.respond(Hub.REPLY_FAIL)
+        except Exception, e:
+            print "exc, dso is", dso
+            self.respond(EndPointException(e).to_json())
             
     def connectionLost(self, reason):
         del self

File tests/hghub/hghub.py

     def remote_repository(self, path, *args, **kwargs):
         return hg.repository(self.ui, path, *args, **kwargs)
 
+    def remote_list(self):
+        return [ 1, 2, 3, 5 ]
+
 class HGHubFactory(HubFactory):
     protocol = HGHub