jsonrpc: Lookup methods using the objects map directly

This commit is contained in:
Stein Magnus Jodal 2012-11-25 20:36:04 +01:00
parent 5a05b4af9d
commit 7f987cb1e2
2 changed files with 21 additions and 42 deletions

View File

@ -37,10 +37,7 @@ class JsonRpcWrapper(object):
hello -> lambda
abc.def -> abc.def()
Only the public methods of the objects 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.
Only the public methods of the mounted objects will be exposed.
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.
@ -59,32 +56,10 @@ class JsonRpcWrapper(object):
"""
def __init__(self, objects, decoders=None, encoders=None):
self.obj = self._build_exported_object(objects)
self.objects = objects
self.decoder = get_combined_json_decoder(decoders or [])
self.encoder = get_combined_json_encoder(encoders or [])
def _build_exported_object(self, objects):
class EmptyObject(object):
pass
if '' in objects:
exported_object = objects['']
else:
exported_object = EmptyObject()
mounts = sorted(objects.keys(), key=lambda x: len(x))
for mount in mounts:
parent = exported_object
path = mount.split('.')
for part in path[:-1]:
if not hasattr(parent, part):
setattr(parent, part, EmptyObject())
parent = getattr(parent, part)
if path[-1]:
setattr(parent, path[-1], objects[mount])
return exported_object
def handle_json(self, request):
"""
Handles an incoming request encoded as a JSON string.
@ -205,15 +180,21 @@ class JsonRpcWrapper(object):
raise JsonRpcInvalidRequestError(
data='"params", if given, must be an array or an object')
def _get_method(self, name):
def _get_method(self, method_path):
if inspect.isroutine(self.objects.get(method_path, None)):
# The mounted object is the callable
return self.objects[method_path]
# The mounted object contains the callable
if '.' in method_path:
mount, method_name = method_path.rsplit('.', 1)
else:
mount, method_name = '', method_path
try:
path = name.split('.')
this = self.obj
for part in path:
if part.startswith('_'):
raise AttributeError
this = getattr(this, part)
return this
if method_name.startswith('_'):
raise AttributeError
obj = self.objects[mount]
return getattr(obj, method_name)
except (AttributeError, KeyError):
raise JsonRpcMethodNotFoundError()
@ -309,13 +290,9 @@ class JsonRpcInspector(object):
'abc': Abc,
})
Since this inspector is based on inspecting classes and not instances, it
will not give you a complete picture of what is actually exported by
:class:`JsonRpcWrapper`. In particular:
- it will not include methods added dynamically, and
- it will not include public methods on attributes on the instances that
are to be exposed.
Since the inspector is based on inspecting classes and not instances, it
will not include methods added dynamically. The wrapper works with
instances, and it will thus export dynamically added methods as well.
:param objects: mapping between mounts and exposed functions or classes
:type objects: dict

View File

@ -45,6 +45,8 @@ class JsonRpcTestBase(unittest.TestCase):
objects={
'hello': lambda: 'Hello, world!',
'core': self.core,
'core.playback': self.core.playback,
'core.tracklist': self.core.tracklist,
'': Calculator(),
},
encoders=[models.ModelJSONEncoder],