Permalink
Browse files

Merging spanezz changes, providing alternate async tests.

  • Loading branch information...
2 parents 00aea69 + 7f0ca8b commit 79918dabbdca033388fd4297ca9140b995572964 @joshmarshall committed Jan 25, 2014
Showing with 115 additions and 18 deletions.
  1. +16 −9 tests/helpers.py
  2. +94 −0 tests/test_async.py
  3. +0 −1 tests/test_xml.py
  4. +5 −8 tornadorpc/base.py
View
@@ -1,7 +1,7 @@
import threading
import time
-from tornado.httpclient import AsyncHTTPClient
from tornadorpc import start_server, private, async
+from tornado.httpclient import AsyncHTTPClient
class Tree(object):
@@ -22,14 +22,6 @@ class TestHandler(object):
def add(self, x, y):
return x+y
- @async
- def async(self, url):
- async_client = AsyncHTTPClient()
- async_client.fetch(url, self._handle_response)
-
- def _handle_response(self, response):
- self.result(response.code)
-
@private
def private(self):
# Should not be callable
@@ -42,13 +34,25 @@ def _private(self):
def internal_error(self):
raise Exception("Yar matey!")
+ @async
+ def async(self, url):
+ async_client = AsyncHTTPClient()
+ async_client.fetch(url, self._handle_response)
+
+ def _handle_response(self, response):
+ self.result(response.code)
+
class TestServer(object):
threads = {}
@classmethod
def start(cls, handler, port):
+ # threading, while functional for testing the built-in python
+ # clients, is an overly complicated solution for IOLoop based
+ # servers. After implementing a tornado-based JSON-RPC client
+ # and XML-RPC client, move this to an IOLoop based test case.
if not cls.threads.get(port):
cls.threads[port] = threading.Thread(
target=start_server,
@@ -65,9 +69,11 @@ class RPCTests(object):
server = None
handler = None
+ io_loop = None
port = 8002
def setUp(self):
+ super(RPCTests, self).setUp()
self.server = TestServer.start(self.handler, self.port)
def get_url(self):
@@ -87,6 +93,7 @@ def test_add(self):
self.assertEqual(result, 11)
def test_async(self):
+ # this should be refactored to use Async RPC clients...
url = 'http://www.google.com'
client = self.get_client()
result = client.async(url)
View
@@ -0,0 +1,94 @@
+import json
+from tests.helpers import TestHandler
+from tornado.httpclient import AsyncHTTPClient
+from tornado.testing import AsyncHTTPTestCase
+import tornado.web
+from tornadorpc import async
+from tornadorpc.xml import XMLRPCHandler
+import xmlrpclib
+
+
+class AsyncHandler(XMLRPCHandler, TestHandler):
+
+ @async
+ def async_method(self, url):
+ async_client = AsyncHTTPClient()
+ async_client.fetch(url, self._handle_response)
+
+ @async
+ def bad_async_method(self, url):
+ async_client = AsyncHTTPClient()
+ async_client.fetch(url, self._handle_response)
+ return 5
+
+ def _handle_response(self, response):
+ self.result(json.loads(response.body))
+
+
+class AsyncXMLRPCClient(object):
+
+ def __init__(self, url, ioloop, fetcher):
+ self._url = url
+ self._ioloop = ioloop
+ self._fetcher = fetcher
+
+ def __getattr__(self, attribute):
+ return Caller(attribute, self)
+
+ def execute(self, method, params, keyword_params):
+ if params and keyword_params:
+ raise Exception(
+ "Can't have both keyword and positional arguments.")
+ arguments = params or keyword_params
+ body = xmlrpclib.dumps(arguments, methodname=method)
+ response = self._fetcher(self._url, method="POST", body=body)
+ result, _ = xmlrpclib.loads(response.body)
+ return result[0]
+
+
+class Caller(object):
+
+ def __init__(self, namespace, client):
+ self._namespace = namespace
+ self._client = client
+
+ def __getattr__(self, namespace):
+ self._namespace += "." + namespace
+ return self
+
+ def __call__(self, *args, **kwargs):
+ return self._client.execute(self._namespace, args, kwargs)
+
+
+class AsyncTests(AsyncHTTPTestCase):
+
+ def get_app(self):
+
+ class IndexHandler(tornado.web.RequestHandler):
+
+ def get(self):
+ self.finish({"foo": "bar"})
+
+ return tornado.web.Application([
+ ("/", IndexHandler),
+ ("/RPC2", AsyncHandler)
+ ])
+
+ def get_client(self):
+ return AsyncXMLRPCClient(
+ url="/RPC2", ioloop=self.io_loop, fetcher=self.fetch)
+
+ def test_async_method(self):
+ client = self.get_client()
+ result = client.async_method(
+ "http://localhost:%d/" % (self.get_http_port()))
+ self.assertEqual({"foo": "bar"}, result)
+
+ def test_async_returns_non_none_raises_internal_error(self):
+ client = self.get_client()
+ try:
+ client.bad_async_method(
+ "http://localhost:%d/" % (self.get_http_port()))
+ self.fail("xmlrpclib.Fault should have been raised.")
+ except xmlrpclib.Fault, fault:
+ self.assertEqual(-32603, fault.faultCode)
View
@@ -54,7 +54,6 @@ def test_internal_error(self):
def test_parse_error(self):
try:
- print self.get_url()
urllib2.urlopen(self.get_url(), '<garbage/>')
except xmlrpclib.Fault, f:
self.assertEqual(-32700, f.faultCode)
View
@@ -8,7 +8,6 @@
You can use the utility functions like 'private' and 'start_server'.
"""
-import logging
from tornado.web import RequestHandler
import tornado.web
import tornado.ioloop
@@ -100,7 +99,7 @@ def dispatch(self, method_name, params):
Handler class. Currently supports only positional
or keyword arguments, not mixed.
"""
- if method_name in dir(RequestHandler):
+ if hasattr(RequestHandler, method_name):
# Pre-existing, not an implemented attribute
return self.handler.result(self.faults.method_not_found())
method = self.handler
@@ -116,7 +115,7 @@ def dispatch(self, method_name, params):
# Not callable, so not a method
return self.handler.result(self.faults.method_not_found())
if method_name.startswith('_') or \
- ('private' in dir(method) and method.private is True):
+ getattr(method, 'private', False) is True:
# No, no. That's private.
return self.handler.result(self.faults.method_not_found())
args = []
@@ -141,14 +140,12 @@ def dispatch(self, method_name, params):
self.traceback(method_name, params)
return self.handler.result(self.faults.internal_error())
- if 'async' in dir(method) and method.async:
+ if getattr(method, 'async', False):
# Asynchronous response -- the method should have called
# self.result(RESULT_VALUE)
if response is not None:
# This should be deprecated to use self.result
- message = "Async results should use 'self.result()'"
- message += " Return result will be ignored."
- logging.warning(message)
+ return self.handler.result(self.faults.internal_error())
else:
# Synchronous result -- we call result manually.
return self.handler.result(response)
@@ -227,7 +224,7 @@ def check_method(self, attr_name, obj):
raise AttributeError('Private object or method.')
attr = getattr(obj, attr_name)
- if 'private' in dir(attr) and attr.private:
+ if getattr(attr, 'private', False):
raise AttributeError('Private object or method.')
return attr

0 comments on commit 79918da

Please sign in to comment.