Commits

Atsushi Odagiri committed ad8853a

fixed and passed tests of examples.

Comments (0)

Files changed (3)

src/jsonrpc/jsonrpc/__init__.py

 from webob import exc
 
 class JsonRpcApplication(object):
-    def __init__(self, methods):
-        self.methods = methods
+    def __init__(self, methods=None):
+        if methods is not None:
+            self.methods = methods
+        else:
+            self.methods = {}
+
+    def addModule(self, mod):
+        name = mod.__name__
+        for k, v in ((k, v) for k, v in mod.__dict__.iteritems() if not k.startswith('_') and callable(v)):
+            self.add(name + '.' + k, v)
+
+    def add(self, name, func):
+        self.methods[name] = func
 
     def process(self, data):
-        if data['jsonrpc'] != "2.0":
+
+        if data.get('jsonrpc') != "2.0":
             return {'jsonrpc':'2.0',
                     'id':data.get('id'),
                     'error':{'code':INVALID_REQUEST,
                     'id':data.get('id'),
                     'error':{'code':INVALID_REQUEST,
                              'message':errors[INVALID_REQUEST]}}
-
+        
         methodname = data['method']
-
+        if not isinstance(methodname, basestring):
+            return {'jsonrpc':'2.0',
+                    'id':data.get('id'),
+                    'error':{'code':INVALID_REQUEST,
+                             'message':errors[INVALID_REQUEST]}}
+            
         if methodname.startswith('_'):
             return {'jsonrpc':'2.0',
                     'id':data.get('id'),
             if isinstance(params, list):
                 result = method(*params)
             elif isinstance(params, dict):
-                result = method(**params)
+                result = method(**dict([(str(k), v) for k, v in params.iteritems()]))
             else:
                 return {'jsonrpc':'2.0',
                         'id':data.get('id'),
                         'error':{'code':INVALID_REQUEST,
                                  'message':errors[INVALID_REQUEST]}}
-            
-            resdata = {
-                'jsonrpc':'2.0',
-                'id':data['id'],
-                'result':result,
-            }
+            resdata = None
+            if data.get('id'):
+
+                resdata = {
+                    'jsonrpc':'2.0',
+                    'id':data.get('id'),
+                    'result':result,
+                    }
             return resdata
         except Exception, e:
             return {'jsonrpc':'2.0',
 
         if request.content_type != 'application/json':
             raise exc.HTTPBadRequest(body='Content-type must by application/json')
+        try:
+            data = json.loads(request.body)
+        except ValueError, e:
+            resdata = {'jsonrpc':'2.0',
+                       'id':None,
+                       'error':{'code':PARSE_ERROR,
+                                'message':errors[PARSE_ERROR]}}
 
-        data = json.loads(request.body)
-        if isinstance(data, dict):
-            resdata = self.process(data)
-        elif isinstance(data, list):
-            resdata = [self.process(d) for d in data]
+        else:
             
-        
+            if isinstance(data, dict):
+                resdata = self.process(data)
+            elif isinstance(data, list):
+                if len([x for x in data if not isinstance(x, dict)]):
+                    resdata = {'jsonrpc':'2.0',
+                               'id':None,
+                               'error':{'code':INVALID_REQUEST,
+                                        'message':errors[INVALID_REQUEST]}}
+                else:
+                    resdata = [d for d in (self.process(d) for d in data) if d is not None]
+            
+
         response = Response(content_type="application/json")
 
-        response.body = json.dumps(resdata)
+        if resdata:
+            response.body = json.dumps(resdata)
         return response(environ, start_response)
 
 def make_app(global_conf, **app_conf):

src/jsonrpc/tests/__init__.py

+# -*- coding:utf-8 -*-
+
 import simplejson as json
 from webtest import TestApp
 from jsonrpc import JsonRpcApplication
 def subtract(minuend, subtrahend):
     return minuend - subtrahend
 
+def greeting(name):
+    return "Hello, %s!" % name
+
+def update(*args):
+    pass
+
+def notify_hello(*args):
+    pass
+
+def get_data(*args):
+    return ["hello", 5]
+
 def createapp():
-    app = JsonRpcApplication(dict(subtract=subtract))
+    app = JsonRpcApplication(dict(subtract=subtract, update=update, notify_hello=notify_hello, get_data=get_data))
     app = TestApp(app)
     return app
 
     assert data['result'] == 19
     assert data['id'] == 4
 
+def test_unicode_params():
+    """
+    --> {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}
+    
+    <-- {"jsonrpc": "2.0", "result": 19, "id": 4}
+    """
+    app = JsonRpcApplication(dict(greeting=greeting))
+    app = TestApp(app)
+    data = {"jsonrpc": "2.0", "method": "greeting", "params": {u"name":u"あ"}, "id": 4}
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    print res.body
+    data = res.json
+    assert data['jsonrpc'] == '2.0'
+    assert data['result'] == u"Hello, あ!"
+    assert data['id'] == 4
 
-"""
-a Notification:
+def test_notification():
+    """
+    a Notification:
+    
+    --> {"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}
+    """
+    data = {"jsonrpc": "2.0", "method": "update", "params": [1, 2, 3, 4, 5]}
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    print res.body
 
---> {"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}
---> {"jsonrpc": "2.0", "method": "foobar"}
-"""
+    assert len(res.body) == 0
 
-"""
-rpc call of non-existent method:
 
---> {"jsonrpc": "2.0", "method": "foobar", "id": "1"}
-<-- {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Procedure not found."}, "id": "1"}
-"""
+def test_non_existent():
+    """
+    rpc call of non-existent method:
+    
+    --> {"jsonrpc": "2.0", "method": "foobar", "id": "1"}
+    <-- {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Procedure not found."}, "id": "1"}
+    """
+    data = {"jsonrpc": "2.0", "method": "foobar", "id":"1"}
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    print res.body
+    d = res.json
+    assert d.get('error')
+    assert d['error']['code'] == -32601
 
-"""
-rpc call with invalid JSON:
 
---> {"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]
-<-- {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": null}
-"""
+def test_invalid_json():
+    """
+    rpc call with invalid JSON:
+    
+    --> {"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]
+    <-- {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": null}
+    """
 
-"""
-rpc call with invalid Request object:
+    res = app.post('/', params='{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]',
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    print res.body
+    d = res.json
+    assert d.get('error')
+    assert d['id'] is None
+    assert d['error']['code'] == -32700
 
---> {"jsonrpc": "2.0", "method": 1, "params": "bar"}
-<-- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
-"""
+def test_invalid_request_object():
+    """
+    rpc call with invalid Request object:
+    
+    --> {"jsonrpc": "2.0", "method": 1, "params": "bar"}
+    <-- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
+    """
+    data = {"jsonrpc":"2.0", "method":1, "params":"bar"}
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    print res.body
+    d = res.json
+    assert d.get('error')
+    assert d['id'] is None
+    assert d['error']['code'] == -32600
 
-"""
-rpc call with invalid Batch:
+def test_invalid_batch():
+    """
+    rpc call with invalid Batch:
+    
+    --> [1,2,3]
+    
+    <-- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
+    """
 
---> [1,2,3]
+    data = [1, 2, 3]
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    print res.body
+    d = res.json
+    assert d.get('error')
+    assert d['id'] is None
+    assert d['error']['code'] == -32600
 
-<-- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
-"""
 
-"""
-rpc call Batch:
+def test_rpc_call_batch():
+    """
+    rpc call Batch:
+    
+    --> [
+    {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
+    {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
+    {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
+    {"foo": "boo"},
+    {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
+    {"jsonrpc": "2.0", "method": "get_data", "id": "9"} 
+    ]
+    
+    <-- [
+    {"jsonrpc": "2.0", "result": 7, "id": "1"},
+    {"jsonrpc": "2.0", "result": 19, "id": "2"},
+    {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null},
+    {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, id: "5"},
+    {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
+    ]
+    """
 
---> [
+    data = [
         {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
         {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
         {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
         {"foo": "boo"},
         {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
         {"jsonrpc": "2.0", "method": "get_data", "id": "9"} 
-    ]
+        ]
 
-<-- [
-        {"jsonrpc": "2.0", "result": 7, "id": "1"},
-        {"jsonrpc": "2.0", "result": 19, "id": "2"},
-        {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null},
-        {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, id: "5"},
-        {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
-    ]
-"""
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    d = res.json
+    print len(d)
+    assert len(d) == 5
+    assert d[0]['id'] == '1'
+    assert d[1]['id'] == '2'
+    assert d[2]['id'] is None
+    assert d[2]['error']['code'] == -32600
+    assert d[3]['id'] == '5'
+    assert d[3]['error']['code'] == -32601
+    assert d[4]['id'] == '9'
+    assert d[4]['result'][0] == "hello"
+    assert d[4]['result'][1] == 5
 
-"""
-rpc call Batch, invalid JSON:
+def test_rpc_call_batch_invalid_json():
+    """
+    rpc call Batch, invalid JSON:
+    
+    --> [ {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},{"jsonrpc": "2.0", "method" ]
+    <-- {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": null}
+    
+    
+    """
+    res = app.post('/', params='[ {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},{"jsonrpc": "2.0", "method" ]',
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    d = res.json
+    assert d['error']['code'] == -32700
+    
 
---> [ {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},{"jsonrpc": "2.0", "method" ]
-<-- {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": null}
+def test_add_module():
+    app = JsonRpcApplication()
+    import sample
+    app.addModule(sample)
+    print app.methods
+    assert len(app.methods) == 1
+    app = TestApp(app)
+    data = {"jsonrpc": "2.0", "method": "tests.sample.greeting", "params": {u"n":u"あ"}, "id": 4}
+    res = app.post('/', params=json.dumps(data),
+                   extra_environ={'CONTENT_TYPE':'application/json'})
+    assert res.status_int == 200
+    d = res.json
+    assert d.get('error') is None
+    print d['result'].encode('cp932')
+    assert d['result'] == u'Hello, あ'
 
-
-"""

src/jsonrpc/tests/sample.py

+# -*- coding:utf-8 -*-
+def greeting(n):
+    return "Hello, %s" % n