1. Atsushi Odagiri
  2. microapps

Commits

Atsushi Odagiri  committed 66d11c6

jsonrpc

  • Participants
  • Branches default

Comments (0)

Files changed (5)

File bootstrap.py

View file
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+from optparse import OptionParser
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+# parsing arguments
+parser = OptionParser()
+parser.add_option("-v", "--version", dest="version",
+                          help="use a specific zc.buildout version")
+parser.add_option("-d", "--distribute",
+                   action="store_true", dest="distribute", default=False,
+                   help="Use Disribute rather than Setuptools.")
+
+options, args = parser.parse_args()
+
+if options.version is not None:
+    VERSION = '==%s' % options.version
+else:
+    VERSION = ''
+
+USE_DISTRIBUTE = options.distribute
+args = args + ['bootstrap']
+
+to_reload = False
+try:
+    import pkg_resources
+    if not hasattr(pkg_resources, '_distribute'):
+        to_reload = True
+        raise ImportError
+except ImportError:
+    ez = {}
+    if USE_DISTRIBUTE:
+        exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py'
+                         ).read() in ez
+        ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True)
+    else:
+        exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                             ).read() in ez
+        ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    if to_reload:
+        reload(pkg_resources)
+    else:
+        import pkg_resources
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    def quote (c):
+        return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws  = pkg_resources.working_set
+
+if USE_DISTRIBUTE:
+    requirement = 'distribute'
+else:
+    requirement = 'setuptools'
+
+if is_jython:
+    import subprocess
+
+    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+           quote(tmpeggs), 'zc.buildout' + VERSION],
+           env=dict(os.environ,
+               PYTHONPATH=
+               ws.find(pkg_resources.Requirement.parse(requirement)).location
+               ),
+           ).wait() == 0
+
+else:
+    assert os.spawnle(
+        os.P_WAIT, sys.executable, quote (sys.executable),
+        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
+        dict(os.environ,
+            PYTHONPATH=
+            ws.find(pkg_resources.Requirement.parse(requirement)).location
+            ),
+        ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout' + VERSION)
+import zc.buildout.buildout
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)

File buildout.cfg

View file
+[buildout]
+parts =
+    eggs
+develop =
+    src/jsonrpc
+
+[eggs]
+recipe = zc.recipe.egg
+eggs =
+    jsonrpc
+    nose
+    WebTest
+
+

File src/jsonrpc/jsonrpc/__init__.py

View file
+# -*- coding:utf-8 -*-
+import itertools
+from webob import Request, Response
+from webob import exc
+import simplejson as json
+
+
+class JsonRpcApplication(object):
+    def __init__(self, objmap):
+        self.objmap = objmap
+
+    def process(self, data):
+        if 'method' not in data:
+            raise exc.HTTPBadRequest
+        if data['method'].count('.') != 1:
+            return {
+                'error':{
+                    'message':"invalid method name.('.' is not included or too many.)"
+                    }
+                }
+        objname, methodname = data['method'].split('.')
+        obj = self.objmap[objname]
+
+        if methodname.startswith('_'):
+            return {
+                'error':{
+                    'message':methodname + " is not found."
+                    }
+                }
+
+
+        if not hasattr(obj, methodname):
+            return {
+                'error':{
+                    'message':methodname + " is not found."
+                    }
+                }
+
+        method = getattr(obj, methodname)
+        try:
+            result = method(*data.get('params', []))
+            resdata = {
+                'errors':[],
+                'result':result,
+            }
+        except Exception, e:
+            resdata = {
+                'errors':[str(e)],
+            }
+        return resdata
+
+
+    def methodlist(self, environ, start_response):
+        methods = itertools.chain(*[[n + "." + m 
+                                     for m in o.__class__.__dict__
+                                     if (not m.startswith('_') 
+                                         and callable(getattr(o, m)))]
+                                   for n, o in self.objmap.iteritems()])
+        data = json.dumps(list(methods))
+        res = Response(content_type='application/json',
+                       body=data)
+        return res(environ, start_response)
+            
+
+    def __call__(self, environ, start_response):
+        request = Request(environ)
+        if request.method != "POST":
+            return self.methodlist(environ, start_response)
+        if request.content_type != 'application/json':
+            raise exc.HTTPBadRequest(body='Content-type must by application/json')
+
+        data = json.loads(request.body)
+
+        resdata = dict([(reqid, self.process(data[reqid]))
+                       for reqid in data])
+            
+        
+        response = Response(content_type="application/json")
+
+        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

View file
+from setuptools import setup
+
+setup(
+    name="jsonrpc",
+    version="0.1",
+    install_requires=[
+        "simplejson",
+        "WebOb",
+    ],
+    entry_points=
+        {"paste.app_factory":["main=jsonrpc:make_app"]}
+)
+

File src/jsonrpc/tests/__init__.py

View file
+import simplejson as json
+from webtest import TestApp
+from jsonrpc import JsonRpcApplication
+from webob import exc
+
+class TestObj(object):
+    def greeting(self, name):
+        return "Hello, %s" % name
+
+    def dummy(self):
+        return "dummy"
+
+    def add(self, a, b):
+        return dict(a=a, b=b, added=a+b)
+
+    def _notcallable(self):
+        pass
+
+
+def test_refrection():
+    app = TestApp(JsonRpcApplication(dict(greeting=TestObj())))
+    res = app.get('/')
+    data =res.json
+    print res.body
+    assert len(data) == 3
+    assert 'greeting.greeting' in data
+    assert 'greeting.add' in data
+
+
+def test_greeting():
+    app = TestApp(JsonRpcApplication(dict(greeting=TestObj())))
+    data = {
+        "greeting-test":{
+            "method":"greeting.greeting",
+            "params":['aodag'],
+        },
+    }
+    res = app.post('/', params=json.dumps(data),
+            extra_environ={'CONTENT_TYPE':'application/json'})
+
+    results = res.json
+    assert results['greeting-test']['result'] == "Hello, aodag"
+
+        
+def test_with_params():
+    app = TestApp(JsonRpcApplication(dict(greeting=TestObj())))
+    data = {
+        "param-test":{
+            "method":"greeting.add",
+            "params":[1, 2],
+        },
+    }
+
+    res = app.post('/', params=json.dumps(data),
+            extra_environ={'CONTENT_TYPE':'application/json'})
+    results = res.json['param-test']
+
+    assert results['result']['a'] == 1
+    assert results['result']['b'] == 2
+    assert results['result']['added'] == 3
+
+def test_batch_request():
+    app = TestApp(JsonRpcApplication(dict(greeting=TestObj())))
+    data = {
+        "param-test1":
+        {
+            "method":"greeting.add",
+            "params":[1, 2],
+        },
+        "param-test2":
+        {
+            "method":"greeting.add",
+            "params":[5, 6],
+        }
+    }
+    res = app.post('/', params=json.dumps(data),
+            extra_environ={'CONTENT_TYPE':'application/json'})
+    results = res.json
+
+def test_content_type():
+    app = TestApp(JsonRpcApplication(dict(greeting=TestObj())))
+    try:
+        res = app.post('/')
+    except exc.HTTPBadRequest, e:
+        print e.body
+        assert e.body == 'Content-type must by application/json'
+    
+def test_invalidmethod():
+    app = TestApp(JsonRpcApplication(dict(greeting=TestObj())))
+    data = {
+        "param-test1":
+        {
+            "method":"greeting.add",
+            "params":[1, 2],
+        },
+        "param-test2":
+        {
+            "method":"greeting.add",
+            "params":[5, 6],
+        },
+        'invalid1':{
+            "method":"greeting._notcallable",
+            },
+        'invalid2':{
+            "method":"greeting.notdefined"
+            },
+        'invalid3':{
+            "method":"invalidname"
+            }
+    }
+    res = app.post('/', params=json.dumps(data),
+            extra_environ={'CONTENT_TYPE':'application/json'})
+    results = res.json
+    assert len(results) == 5
+    assert results['invalid1']['error']
+    assert results['invalid2']['error']
+    assert results['invalid3']['error']