Commits

Atsushi Odagiri  committed e24e653

change package name from jsonrpc to jsonrpc2.

  • Participants
  • Parent commits 9f906e0
  • Tags jsonrpc2_0_1

Comments (0)

Files changed (13)

File buildout.cfg

 parts =
     eggs
 develop =
-    src/jsonrpc
+    src/jsonrpc2
     src/wsgitestapp
 find-links =
     http://pypi.python.org/pypi/docutils
 [eggs]
 recipe = zc.recipe.egg
 eggs =
-    jsonrpc
+    jsonrpc2
     nose
     WebTest
     docutils

File src/jsonrpc/docs/conf.py

-# config for sphinx
-master_doc = 'index'
-source_encoding = 'utf-8'
-project = u'jsonrpc'
-copyright = u'2010, Atsushi Odagiri'

File src/jsonrpc/docs/index.rst

-================
-JSON RPC
-================
-jsonrpcは、JSON RPCによる呼び出しを可能にするWSGIアプリケーションです。
-`JSON RPC Spec2 Draft <http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal>`_
-に準拠させるようにしています。
-JSONパーサーは、json, django.utils.simplejson, simplejsonを順に検索して最初に見つかったものを利用します。
-
-基本的な使い方
-==================================================================
-
->>> from webtest import TestApp
->>> import jsonrpc
-
-addメソッドで、RPCメソッドを追加します。
-
->>> app = jsonrpc.JsonRpcApplication()
->>> app.add('hello', lambda n: "Hello, %s!" % n)
->>> app = TestApp(app)
->>> res = app.post('/', '{"jsonrpc":"2.0", "method":"hello", "params":["aodag"], "id":"x"}', extra_environ={'CONTENT_TYPE':'application/json'})
->>> res.status_int
-200
->>> res.json['id']
-'x'
->>> res.json["result"]
-'Hello, aodag!'
-
-
-モジュール単位で追加可能です。
-tests.sample.py::
-
-  def greeting(n):
-      return "Hello, %s" % n
-
-
->>> import tests.sample
->>> app = jsonrpc.JsonRpcApplication()
->>> app.addModule(tests.sample)
->>> app = TestApp(app)
->>> res = app.post('/', '{"jsonrpc":"2.0", "method":"tests.sample.greeting", "params":["aodag"], "id":"y"}', extra_environ={'CONTENT_TYPE':'application/json'})
->>> res.status_int
-200
->>> res.json['id']
-'y'
->>> res.json["result"]
-'Hello, aodag'
-

File src/jsonrpc/jsonrpc/__init__.py

-# -*- coding:utf-8 -*-
-
-"""
-http://groups.google.com/group/json-rpc/web/json-rpc-2-0
-
-errors:
-
-code 	message 	meaning
--32700 	Parse error 	Invalid JSON was received by the server.
-An error occurred on the server while parsing the JSON text.
--32600 	Invalid Request 	The JSON sent is not a valid Request object.
--32601 	Method not found 	The method does not exist / is not available.
--32602 	Invalid params 	Invalid method parameter(s).
--32603 	Internal error 	Internal JSON-RPC error.
--32099 to -32000 	Server error 	Reserved for implementation-defined server-errors.
-
-"""
-PARSE_ERROR = -32700
-INVALID_REQUEST = -32600
-METHOD_NOT_FOUND = -32601
-INVALID_PARAMS = -32602
-INTERNAL_ERROR = -32603
-errors = {}
-errors[PARSE_ERROR] = "Parse Error"
-errors[INVALID_REQUEST] = "Invalid Request"
-errors[METHOD_NOT_FOUND] = "Method Not Found"
-errors[INVALID_PARAMS] = "Invalid Params"
-errors[INTERNAL_ERROR] = "Internal Error"
-
-try:
-    import json
-except ImportError:
-    try:
-        import django.utils.simplejson as json
-    except ImportError:
-        import simplejson as json
-
-import itertools
-from webob import Request, Response
-from webob import exc
-
-class JsonRpcApplication(object):
-    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.get('jsonrpc') != "2.0":
-            return {'jsonrpc':'2.0',
-                    'id':data.get('id'),
-                    'error':{'code':INVALID_REQUEST,
-                             'message':errors[INVALID_REQUEST]}}
-        if 'method' not in data:
-            return {'jsonrpc':'2.0',
-                    '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'),
-                    'error':{'code':METHOD_NOT_FOUND,
-                             'message':errors[METHOD_NOT_FOUND]}}
-
-
-        if methodname not in self.methods:
-            return {'jsonrpc':'2.0',
-                    'id':data.get('id'),
-                    'error':{'code':METHOD_NOT_FOUND,
-                             'message':errors[METHOD_NOT_FOUND]}}
-
-
-        method = self.methods[methodname]
-        try:
-            params = data.get('params', [])
-            if isinstance(params, list):
-                result = method(*params)
-            elif isinstance(params, dict):
-                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 = None
-            if data.get('id'):
-
-                resdata = {
-                    'jsonrpc':'2.0',
-                    'id':data.get('id'),
-                    'result':result,
-                    }
-            return resdata
-        except Exception, e:
-            return {'jsonrpc':'2.0',
-                    'id':data.get('id'),
-                    'error':{'code':INTERNAL_ERROR,
-                             'message':errors[INTERNAL_ERROR],
-                             'data':str(e)}}
-
-
-
-
-    def __call__(self, environ, start_response):
-        request = Request(environ)
-        if request.method != "POST":
-            raise exc.HTTPMethodNotAllowed
-
-        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]}}
-
-        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")
-
-        if resdata:
-            response.body = json.dumps(resdata)
-        return response(environ, start_response)
-
-def make_app(global_conf, **app_conf):
-    conf = global_conf.copy()
-    conf.update(app_conf)
-
-    modname = conf["module"]
-    __import__(modname)
-    mod = sys.modules[modname]
-    obj = eval(conf["expression"], mod.__dict__)
-    return JsonRpcApplication(obj)
-

File src/jsonrpc/setup.py

-from setuptools import setup
-
-setup(
-    name="jsonrpc",
-    author='Atsushi Odagiri',
-    author_email='aodagx@gmail.com',
-    url='http://bitbucket.org/aodag/microapps/overview/',
-    version="0.1",
-    install_requires=[
-        "simplejson",
-        "WebOb",
-    ],
-    entry_points=
-        {"paste.app_factory":["main=jsonrpc:make_app"]}
-)
-

File src/jsonrpc/tests/__init__.py

-# -*- coding:utf-8 -*-
-
-import simplejson as json
-from webtest import TestApp
-from jsonrpc import JsonRpcApplication
-from webob import exc
-
-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, update=update, notify_hello=notify_hello, get_data=get_data))
-    app = TestApp(app)
-    return app
-
-
-app = createapp()
-
-"""
-rpc call with positional parameters:
-"""
-
-def test_positional_params1():
-    """
-    --> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
-    <-- {"jsonrpc": "2.0", "result": 19, "id": 1}
-    """
-    data = {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
-    
-    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'] == 19
-    assert data['id'] == 1
-
-def test_positional_params2():
-    """
-    --> {"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}
-    
-    <-- {"jsonrpc": "2.0", "result": -19, "id": 2}
-    """
-    data = {"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}
-    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'] == -19
-    assert data['id'] == 2
-
-
-"""
-rpc call with named parameters:
-"""
-def test_named_params1():
-    """
-    --> {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}
-    <-- {"jsonrpc": "2.0", "result": 19, "id": 3}
-    """
-    data = {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}
-    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'] == 19
-    assert data['id'] == 3
-
-
-def test_named_params2():
-    """
-    --> {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}
-    
-    <-- {"jsonrpc": "2.0", "result": 19, "id": 4}
-    """
-    data = {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "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'] == 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
-
-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
-
-    assert len(res.body) == 0
-
-
-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
-
-
-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}
-    """
-
-    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
-
-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
-
-def test_invalid_batch():
-    """
-    rpc call with invalid Batch:
-    
-    --> [1,2,3]
-    
-    <-- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
-    """
-
-    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
-
-
-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"} 
-        ]
-
-    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
-
-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
-    
-
-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, あ'
-

File src/jsonrpc/tests/sample.py

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

File src/jsonrpc2/docs/conf.py

+# config for sphinx
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+master_doc = 'index'
+source_encoding = 'utf-8'
+project = u'jsonrpc'
+copyright = u'2010, Atsushi Odagiri'
+language = "ja"
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
+html_use_modindex = True
+

File src/jsonrpc2/docs/index.rst

+================
+JSON RPC2
+================
+jsonrpc2は、JSON RPCによる呼び出しを可能にするWSGIアプリケーションです。
+`JSON RPC Spec2 Draft <http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal>`_
+に準拠させるようにしています。
+JSONパーサーは、json, django.utils.simplejson, simplejsonを順に検索して最初に見つかったものを利用します。
+
+インストール方法
+==================================================================
+
+easy_install::
+ easy_install -f jsonrpc2
+
+
+基本的な使い方
+==================================================================
+
+>>> from webtest import TestApp
+>>> import jsonrpc2
+
+addメソッドで、RPCメソッドを追加します。
+
+>>> app = jsonrpc2.JsonRpcApplication()
+>>> app.add('hello', lambda n: "Hello, %s!" % n)
+>>> app = TestApp(app)
+>>> res = app.post('/', '{"jsonrpc":"2.0", "method":"hello", "params":["aodag"], "id":"x"}', extra_environ={'CONTENT_TYPE':'application/json'})
+>>> res.status_int
+200
+>>> res.json['id']
+'x'
+>>> res.json["result"]
+'Hello, aodag!'
+
+
+モジュール単位で追加可能です。
+tests.sample.py::
+
+  def greeting(n):
+      return "Hello, %s" % n
+
+
+>>> import tests.sample
+>>> app = jsonrpc2.JsonRpcApplication()
+>>> app.addModule(tests.sample)
+>>> app = TestApp(app)
+>>> res = app.post('/', '{"jsonrpc":"2.0", "method":"tests.sample.greeting", "params":["aodag"], "id":"y"}', extra_environ={'CONTENT_TYPE':'application/json'})
+>>> res.status_int
+200
+>>> res.json['id']
+'y'
+>>> res.json["result"]
+'Hello, aodag'
+

File src/jsonrpc2/jsonrpc2/__init__.py

+# -*- coding:utf-8 -*-
+
+"""
+http://groups.google.com/group/json-rpc/web/json-rpc-2-0
+
+errors:
+
+code 	message 	meaning
+-32700 	Parse error 	Invalid JSON was received by the server.
+An error occurred on the server while parsing the JSON text.
+-32600 	Invalid Request 	The JSON sent is not a valid Request object.
+-32601 	Method not found 	The method does not exist / is not available.
+-32602 	Invalid params 	Invalid method parameter(s).
+-32603 	Internal error 	Internal JSON-RPC error.
+-32099 to -32000 	Server error 	Reserved for implementation-defined server-errors.
+
+"""
+PARSE_ERROR = -32700
+INVALID_REQUEST = -32600
+METHOD_NOT_FOUND = -32601
+INVALID_PARAMS = -32602
+INTERNAL_ERROR = -32603
+errors = {}
+errors[PARSE_ERROR] = "Parse Error"
+errors[INVALID_REQUEST] = "Invalid Request"
+errors[METHOD_NOT_FOUND] = "Method Not Found"
+errors[INVALID_PARAMS] = "Invalid Params"
+errors[INTERNAL_ERROR] = "Internal Error"
+
+try:
+    import json
+except ImportError:
+    try:
+        import django.utils.simplejson as json
+    except ImportError:
+        import simplejson as json
+
+import itertools
+from webob import Request, Response
+from webob import exc
+
+class JsonRpcApplication(object):
+    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.get('jsonrpc') != "2.0":
+            return {'jsonrpc':'2.0',
+                    'id':data.get('id'),
+                    'error':{'code':INVALID_REQUEST,
+                             'message':errors[INVALID_REQUEST]}}
+        if 'method' not in data:
+            return {'jsonrpc':'2.0',
+                    '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'),
+                    'error':{'code':METHOD_NOT_FOUND,
+                             'message':errors[METHOD_NOT_FOUND]}}
+
+
+        if methodname not in self.methods:
+            return {'jsonrpc':'2.0',
+                    'id':data.get('id'),
+                    'error':{'code':METHOD_NOT_FOUND,
+                             'message':errors[METHOD_NOT_FOUND]}}
+
+
+        method = self.methods[methodname]
+        try:
+            params = data.get('params', [])
+            if isinstance(params, list):
+                result = method(*params)
+            elif isinstance(params, dict):
+                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 = None
+            if data.get('id'):
+
+                resdata = {
+                    'jsonrpc':'2.0',
+                    'id':data.get('id'),
+                    'result':result,
+                    }
+            return resdata
+        except Exception, e:
+            return {'jsonrpc':'2.0',
+                    'id':data.get('id'),
+                    'error':{'code':INTERNAL_ERROR,
+                             'message':errors[INTERNAL_ERROR],
+                             'data':str(e)}}
+
+
+
+
+    def __call__(self, environ, start_response):
+        request = Request(environ)
+        if request.method != "POST":
+            raise exc.HTTPMethodNotAllowed
+
+        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]}}
+
+        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")
+
+        if resdata:
+            response.body = json.dumps(resdata)
+        return response(environ, start_response)
+
+def make_app(global_conf, **app_conf):
+    conf = global_conf.copy()
+    conf.update(app_conf)
+
+    modname = conf["module"]
+    __import__(modname)
+    mod = sys.modules[modname]
+    obj = eval(conf["expression"], mod.__dict__)
+    return JsonRpcApplication(obj)
+

File src/jsonrpc2/setup.py

+from setuptools import setup
+version="0.1"
+
+setup(
+    name="jsonrpc2",
+    description="WSGI Framework for JSON RPC 2.0",
+    long_description="""\
+jsonrpc2 is WSGI Framework for JSON RPC 2.0.
+JSON RPC 2.0 Spec can be seen on http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal.
+
+
+""",
+    classifiers=[
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: MIT License",
+        "Topic :: Internet :: WWW/HTTP :: WSGI",
+        "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+        ],
+    author='Atsushi Odagiri',
+    author_email='aodagx@gmail.com',
+    keywords='wsgi request web http',
+    url='http://bitbucket.org/aodag/microapps/overview/',
+    version=version,
+    install_requires=[
+        "simplejson",
+        "WebOb",
+    ],
+)
+

File src/jsonrpc2/tests/__init__.py

+# -*- coding:utf-8 -*-
+
+import simplejson as json
+from webtest import TestApp
+from jsonrpc2 import JsonRpcApplication
+from webob import exc
+
+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, update=update, notify_hello=notify_hello, get_data=get_data))
+    app = TestApp(app)
+    return app
+
+
+app = createapp()
+
+"""
+rpc call with positional parameters:
+"""
+
+def test_positional_params1():
+    """
+    --> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
+    <-- {"jsonrpc": "2.0", "result": 19, "id": 1}
+    """
+    data = {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
+    
+    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'] == 19
+    assert data['id'] == 1
+
+def test_positional_params2():
+    """
+    --> {"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}
+    
+    <-- {"jsonrpc": "2.0", "result": -19, "id": 2}
+    """
+    data = {"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}
+    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'] == -19
+    assert data['id'] == 2
+
+
+"""
+rpc call with named parameters:
+"""
+def test_named_params1():
+    """
+    --> {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}
+    <-- {"jsonrpc": "2.0", "result": 19, "id": 3}
+    """
+    data = {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}
+    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'] == 19
+    assert data['id'] == 3
+
+
+def test_named_params2():
+    """
+    --> {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}
+    
+    <-- {"jsonrpc": "2.0", "result": 19, "id": 4}
+    """
+    data = {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "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'] == 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
+
+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
+
+    assert len(res.body) == 0
+
+
+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
+
+
+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}
+    """
+
+    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
+
+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
+
+def test_invalid_batch():
+    """
+    rpc call with invalid Batch:
+    
+    --> [1,2,3]
+    
+    <-- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
+    """
+
+    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
+
+
+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"} 
+        ]
+
+    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
+
+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
+    
+
+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, あ'
+

File src/jsonrpc2/tests/sample.py

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