From: Ben Darnell Date: Mon, 19 Jan 2015 16:20:45 +0000 (-0500) Subject: Add a convert_yield adapter for twisted Deferreds. X-Git-Tag: v4.1.0b1~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5add5a91806604934bac43db090c3121756c1410;p=thirdparty%2Ftornado.git Add a convert_yield adapter for twisted Deferreds. --- diff --git a/tornado/platform/twisted.py b/tornado/platform/twisted.py index ccb44de8b..09b328366 100644 --- a/tornado/platform/twisted.py +++ b/tornado/platform/twisted.py @@ -70,8 +70,10 @@ import datetime import functools import numbers import socket +import sys import twisted.internet.abstract +from twisted.internet.defer import Deferred from twisted.internet.posixbase import PosixReactorBase from twisted.internet.interfaces import \ IReactorFDSet, IDelayedCall, IReactorTime, IReadDescriptor, IWriteDescriptor @@ -84,6 +86,7 @@ import twisted.names.resolve from zope.interface import implementer +from tornado.concurrent import Future from tornado.escape import utf8 from tornado import gen import tornado.ioloop @@ -564,3 +567,17 @@ class TwistedResolver(Resolver): (resolved_family, (resolved, port)), ] raise gen.Return(result) + +if hasattr(gen.convert_yielded, 'register'): + @gen.convert_yielded.register(Deferred) + def _(d): + f = Future() + def errback(failure): + try: + failure.raiseException() + # Should never happen, but just in case + raise Exception("errback called without error") + except: + f.set_exc_info(sys.exc_info()) + d.addCallbacks(f.set_result, errback) + return f diff --git a/tornado/test/twisted_test.py b/tornado/test/twisted_test.py index 2922a61e0..3ceffa77c 100644 --- a/tornado/test/twisted_test.py +++ b/tornado/test/twisted_test.py @@ -27,7 +27,7 @@ import threading try: import fcntl - from twisted.internet.defer import Deferred + from twisted.internet.defer import Deferred, inlineCallbacks, returnValue from twisted.internet.interfaces import IReadDescriptor, IWriteDescriptor from twisted.internet.protocol import Protocol from twisted.python import log @@ -40,7 +40,7 @@ except ImportError: # The core of Twisted 12.3.0 is available on python 3, but twisted.web is not # so test for it separately. try: - from twisted.web.client import Agent + from twisted.web.client import Agent, readBody from twisted.web.resource import Resource from twisted.web.server import Site have_twisted_web = True @@ -52,6 +52,7 @@ try: except ImportError: import _thread as thread # py3 +from tornado import gen from tornado.httpclient import AsyncHTTPClient from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop @@ -65,6 +66,8 @@ from tornado.web import RequestHandler, Application skipIfNoTwisted = unittest.skipUnless(have_twisted, "twisted module not present") +skipIfNoSingleDispatch = unittest.skipIf( + gen.singledispatch is None, "singledispatch module not present") def save_signal_handlers(): saved = {} @@ -432,6 +435,21 @@ class CompatibilityTests(unittest.TestCase): self.assertTrue(chunks) return ''.join(chunks) + def twisted_coroutine_fetch(self, url, runner): + body = [None] + @gen.coroutine + def f(): + # This is simpler than the non-coroutine version, but it cheats + # by reading the body in one blob instead of streaming it with + # a Protocol. + client = Agent(self.reactor) + response = yield client.request('GET', url) + body[0] = yield readBody(response) + self.stop_loop() + self.io_loop.add_callback(f) + runner() + return body[0] + def testTwistedServerTornadoClientIOLoop(self): self.start_twisted_server() response = self.tornado_fetch( @@ -456,6 +474,37 @@ class CompatibilityTests(unittest.TestCase): 'http://localhost:%d' % self.tornado_port, self.run_reactor) self.assertEqual(response, 'Hello from tornado!') + @skipIfNoSingleDispatch + def testTornadoServerTwistedCoroutineClientIOLoop(self): + self.start_tornado_server() + response = self.twisted_coroutine_fetch( + 'http://localhost:%d' % self.tornado_port, self.run_ioloop) + self.assertEqual(response, 'Hello from tornado!') + + +@skipIfNoSingleDispatch +class ConvertDeferredTest(unittest.TestCase): + def test_success(self): + @inlineCallbacks + def fn(): + if False: + # inlineCallbacks doesn't work with regular functions; + # must have a yield even if it's unreachable. + yield + returnValue(42) + f = gen.convert_yielded(fn()) + self.assertEqual(f.result(), 42) + + def test_failure(self): + @inlineCallbacks + def fn(): + if False: + yield + 1 / 0 + f = gen.convert_yielded(fn()) + with self.assertRaises(ZeroDivisionError): + f.result() + if have_twisted: # Import and run as much of twisted's test suite as possible.