jsonrpc: Support methods on the root object

This commit is contained in:
Stein Magnus Jodal 2012-11-22 15:30:34 +01:00
parent 6e2ffb0820
commit 61d6de2378
2 changed files with 39 additions and 15 deletions

View File

@ -8,18 +8,23 @@ import pykka
class JsonRpcWrapper(object):
"""
Wraps objects and make them accessible through JSON-RPC 2.0 messaging.
Wraps an object and makes it accessible through JSON-RPC 2.0 messaging.
This class takes responsibility of communicating with the objects and
This class takes responsibility of communicating with the object and
processing of JSON-RPC 2.0 messages. The transport of the messages over
HTTP, WebSocket, TCP, or whatever is of no concern to this class.
The objects can either be Pykka actors or plain objects. Only their public
methods will be exposed, not any attributes.
Only the object's public methods will be exposed. Attributes are not
exposed by themself, but public methods on public attributes are exposed,
using dotted paths from the exposed object to the method at the end of the
path.
If a method returns an object with a ``get()`` method, it is assumed to be
a future object. Any futures is completed and their value unwrapped before
the JSON RPC wrapper returns the response.
To expose multiple objects, simply create a new "parent" object and assign
the other objects you want to expose to attributes on the "parent" object.
You then wrap the "parent" object.
If a method returns a :class:`pykka.Future`, the future will be completed
and its value unwrapped before the JSON-RPC wrapper returns the response.
For further details on the JSON-RPC 2.0 spec, see
http://www.jsonrpc.org/specification
@ -27,8 +32,8 @@ class JsonRpcWrapper(object):
:param objects: dict of names mapped to objects to be exposed
"""
def __init__(self, objects, decoders=None, encoders=None):
self.objects = objects
def __init__(self, obj, decoders=None, encoders=None):
self.obj = obj
self.decoder = get_combined_json_decoder(decoders or [])
self.encoder = get_combined_json_encoder(encoders or [])
@ -155,8 +160,7 @@ class JsonRpcWrapper(object):
def _get_method(self, name):
try:
path = name.split('.')
root = path.pop(0)
this = self.objects[root]
this = self.obj
for part in path:
if part.startswith('_'):
raise AttributeError

View File

@ -12,6 +12,10 @@ from mopidy.utils import jsonrpc
from tests import unittest
class ExportedObject(object):
pass
class Calculator(object):
def model(self):
return 'TI83'
@ -36,11 +40,14 @@ class JsonRpcTestBase(unittest.TestCase):
def setUp(self):
self.backend = dummy.DummyBackend.start(audio=None).proxy()
self.core = core.Core.start(backends=[self.backend]).proxy()
exported = ExportedObject()
exported.hello = lambda: 'Hello, world!'
exported.core = self.core
exported.calculator = Calculator()
self.jrw = jsonrpc.JsonRpcWrapper(
objects={
'core': self.core,
'calculator': Calculator(),
},
obj=exported,
encoders=[models.ModelJSONEncoder],
decoders=[models.model_json_decoder])
@ -111,6 +118,19 @@ class JsonRpcSerializationTest(JsonRpcTestBase):
class JsonRpcSingleCommandTest(JsonRpcTestBase):
def test_call_method_on_root(self):
request = {
'jsonrpc': '2.0',
'method': 'hello',
'id': 1,
}
response = self.jrw.handle_data(request)
self.assertEqual(response['jsonrpc'], '2.0')
self.assertEqual(response['id'], 1)
self.assertNotIn('error', response)
self.assertEqual(response['result'], 'Hello, world!')
def test_call_method_on_plain_object(self):
request = {
'jsonrpc': '2.0',