From: Ovidiu Predescu Date: Thu, 14 Jul 2011 23:00:02 +0000 (-0700) Subject: Moved twisted/reactor.py to platform/twistedreactor.py. X-Git-Tag: v2.1.0~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5eaa84b7e335b80f02c9ac53af78de5ef9f9583d;p=thirdparty%2Ftornado.git Moved twisted/reactor.py to platform/twistedreactor.py. twistedreactor.py: - cleaned up logging - don't try to crash if stop has been called (uses _running to check it) - don't add reader and writers if they've already been added - use a NullContext when calling add_handler - call IOLoop.close() to properly close file descriptors - implement run() and instruct the reactor to not install signal handlers. import_test.py: - import twistedreactor too twistedreactor_test.py: - do conditional includes based on twisted - remove superfluous logging - don't use assertGreater - use threading instead of the thread module. Join threads at the end of test. - properly close the file descriptor in Reader/Writer's connectionLost(). - make use of tornado.platform.auto's set_close_exec. - write only one in the pipe, and check for that. --- diff --git a/tornado/twisted/reactor.py b/tornado/platform/twistedreactor.py similarity index 84% rename from tornado/twisted/reactor.py rename to tornado/platform/twistedreactor.py index 4f3d1fc71..54520ead8 100644 --- a/tornado/twisted/reactor.py +++ b/tornado/platform/twistedreactor.py @@ -18,12 +18,14 @@ A twisted-style reactor for the Tornado IOLoop. To use it, add the following to your twisted application: -import tornado.twisted.reactor -tornado.twisted.reactor.install() +import tornado.platform.twistedreactor +tornado.platform.twistedreactor.install() from twisted.internet import reactor """ -import errno, functools, sys +import functools +import logging +import sys import time from twisted.internet.base import DelayedCall @@ -35,6 +37,7 @@ from zope.interface import implements import tornado import tornado.ioloop +from tornado.stack_context import NullContext from tornado.ioloop import IOLoop class TornadoDelayedCall(object): @@ -57,7 +60,7 @@ class TornadoDelayedCall(object): try: self._func() except: - print "reactor.py _called caught exception: %s" % sys.exc_info()[0] + logging.error("_called caught exception", exc_info=True) def getTime(self): return self._time @@ -96,7 +99,7 @@ class TornadoReactor(PosixReactorBase): self._writers = {} self._fds = {} # a map of fd to a (reader, writer) tuple self._delayedCalls = {} - # self._waker = None + self._running = False PosixReactorBase.__init__(self) # IReactorTime @@ -144,6 +147,9 @@ class TornadoReactor(PosixReactorBase): """ Add a FileDescriptor for notification of data available to read. """ + if reader in self._readers: + # Don't add the reader if it's already there + return self._readers[reader] = True fd = reader.fileno() if fd in self._fds: @@ -154,13 +160,17 @@ class TornadoReactor(PosixReactorBase): # update it for read events as well. self._ioloop.update_handler(fd, IOLoop.READ | IOLoop.WRITE) else: - self._fds[fd] = (reader, None) - self._ioloop.add_handler(fd, self._invoke_callback, IOLoop.READ) + with NullContext(): + self._fds[fd] = (reader, None) + self._ioloop.add_handler(fd, self._invoke_callback, + IOLoop.READ) def addWriter(self, writer): """ Add a FileDescriptor for notification of data available to write. """ + if writer in self._writers: + return self._writers[writer] = True fd = writer.fileno() if fd in self._fds: @@ -171,8 +181,10 @@ class TornadoReactor(PosixReactorBase): # update it for write events as well. self._ioloop.update_handler(fd, IOLoop.READ | IOLoop.WRITE) else: - self._fds[fd] = (None, writer) - self._ioloop.add_handler(fd, self._invoke_callback, IOLoop.WRITE) + with NullContext(): + self._fds[fd] = (None, writer) + self._ioloop.add_handler(fd, self._invoke_callback, + IOLoop.WRITE) def removeReader(self, reader): """ @@ -227,22 +239,39 @@ class TornadoReactor(PosixReactorBase): """ Implement L{IReactorCore.stop}. """ + self._running = False PosixReactorBase.stop(self) self.runUntilCurrent() - self._ioloop.stop() + try: + self._ioloop.stop() + self._ioloop.close() + except: + # Ignore any exceptions thrown by IOLoop + pass def crash(self): + if not self._running: + return + self._running = False PosixReactorBase.crash(self) self.runUntilCurrent() - self._ioloop.stop() + try: + self._ioloop.stop() + self._ioloop.close() + except: + # Ignore any exceptions thrown by IOLoop + pass def doIteration(self, delay): raise NotImplementedError("doIteration") def mainLoop(self): - self.running = True + self._running = True self._ioloop.start() + def run(self): + PosixReactorBase.run(self, installSignalHandlers=False) + def install(ioloop=None): """ Install the Tornado reactor. diff --git a/tornado/test/import_test.py b/tornado/test/import_test.py index 2275a1165..59734503a 100644 --- a/tornado/test/import_test.py +++ b/tornado/test/import_test.py @@ -9,6 +9,7 @@ class ImportTest(unittest.TestCase): import tornado.autoreload # import tornado.curl_httpclient # depends on pycurl # import tornado.database # depends on MySQLdb + # import tornado.platform.twistedreactor # depends on twisted import tornado.escape import tornado.httpclient import tornado.httpserver diff --git a/tornado/test/twistedreactor_test.py b/tornado/test/twistedreactor_test.py index 7f9a383be..cc4e2f1f7 100644 --- a/tornado/test/twistedreactor_test.py +++ b/tornado/test/twistedreactor_test.py @@ -21,19 +21,21 @@ import fcntl import os import sys import thread +import threading import unittest -from twisted.internet.interfaces import IReadDescriptor, IWriteDescriptor -from twisted.python import log -from tornado.platform.auto import Waker +try: + import twisted + from twisted.internet.interfaces import IReadDescriptor, IWriteDescriptor + from tornado.platform.twistedreactor import TornadoReactor + from zope.interface import implements +except ImportError: + twisted = None + IReadDescriptor = IWriteDescriptor = None + def implements(f): pass from tornado.ioloop import IOLoop -from tornado.twisted.reactor import TornadoReactor -from tornado.testing import AsyncTestCase, LogTrapTestCase - -from zope.interface import implements - -log.startLogging(sys.stdout) +from tornado.platform.auto import set_close_exec class ReactorWhenRunningTest(unittest.TestCase): def setUp(self): @@ -67,7 +69,7 @@ class ReactorCallLaterTest(unittest.TestCase): self.assertEqual(self._reactor.getDelayedCalls(), [dc]) self._reactor.run() self.assertTrue(self._laterCalled) - self.assertGreater(self._called - self._now, self._timeout) + self.assertTrue(self._called - self._now > self._timeout) self.assertEqual(self._reactor.getDelayedCalls(), []) def callLaterCallback(self): @@ -92,8 +94,8 @@ class ReactorTwoCallLaterTest(unittest.TestCase): self._reactor.run() self.assertTrue(self._later1Called) self.assertTrue(self._later2Called) - self.assertGreater(self._called1 - self._now, self._timeout1) - self.assertGreater(self._called2 - self._now, self._timeout2) + self.assertTrue(self._called1 - self._now > self._timeout1) + self.assertTrue(self._called2 - self._now > self._timeout2) self.assertEqual(self._reactor.getDelayedCalls(), []) def callLaterCallback1(self): @@ -110,8 +112,11 @@ class ReactorCallFromThreadTest(unittest.TestCase): self._reactor = TornadoReactor(IOLoop()) self._mainThread = thread.get_ident() - def _newThreadRun(self, a, b): - self.assertEqual(self._thread, thread.get_ident()) + def tearDown(self): + self._thread.join() + + def _newThreadRun(self): + self.assertEqual(self._thread.ident, thread.get_ident()) self._reactor.callFromThread(self._fnCalledFromThread) def _fnCalledFromThread(self): @@ -119,7 +124,8 @@ class ReactorCallFromThreadTest(unittest.TestCase): self._reactor.stop() def _whenRunningCallback(self): - self._thread = thread.start_new_thread(self._newThreadRun, (None, None)) + self._thread = threading.Thread(target=self._newThreadRun) + self._thread.start() def testCallFromThread(self): self._reactor.callWhenRunning(self._whenRunningCallback) @@ -154,7 +160,7 @@ class Reader: return self._fd.fileno() def connectionLost(self, reason): - return + self._fd.close() def doRead(self): self._callback(self._fd) @@ -172,16 +178,12 @@ class Writer: return self._fd.fileno() def connectionLost(self, reason): - return + self._fd.close() def doWrite(self): self._callback(self._fd) class ReactorReaderWriterTest(unittest.TestCase): - def _set_close_exec(self, fd): - flags = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) - def _set_nonblocking(self, fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) @@ -191,8 +193,8 @@ class ReactorReaderWriterTest(unittest.TestCase): r, w = os.pipe() self._set_nonblocking(r) self._set_nonblocking(w) - self._set_close_exec(r) - self._set_close_exec(w) + set_close_exec(r) + set_close_exec(w) self._p1 = os.fdopen(r, "rb", 0) self._p2 = os.fdopen(w, "wb", 0) @@ -201,28 +203,40 @@ class ReactorReaderWriterTest(unittest.TestCase): In this test the writer writes an 'x' to its fd. The reader reads it, check the value and ends the test. """ + self.shouldWrite = True def checkReadInput(fd): - self.assertTrue(fd.read().startswith('x')) + self.assertEquals(fd.read(), 'x') self._reactor.stop() + def writeOnce(fd): + if self.shouldWrite: + self.shouldWrite = False + fd.write('x') self._reader = Reader(self._p1, checkReadInput) - self._writer = Writer(self._p2, lambda fd: fd.write('x')) + self._writer = Writer(self._p2, writeOnce) + + # Test that adding and removing the writer doesn't cause + # unintended effects. self._reactor.addWriter(self._writer) self._reactor.removeWriter(self._writer) self._reactor.addWriter(self._writer) - # Test the add/remove reader functionality - self._reactor.addReader(self._writer) - self._reactor.removeReader(self._writer) + # Test that adding and removing the reader doesn't cause + # unintended effects. self._reactor.addReader(self._reader) self._reactor.removeReader(self._reader) self._reactor.addReader(self._reader) - # Test the add/remove writer functionality - self._reactor.addWriter(self._reader) - self._reactor.removeWriter(self._reader) def testReadWrite(self): self._reactor.callWhenRunning(self._testReadWrite) self._reactor.run() +if twisted is None: + del ReactorWhenRunningTest + del ReactorCallLaterTest + del ReactorTwoCallLaterTest + del ReactorCallFromThreadTest + del ReactorCallInThread + del ReactorReaderWriterTest + if __name__ == "__main__": unittest.main() diff --git a/tornado/twisted/__init__.py b/tornado/twisted/__init__.py deleted file mode 100644 index e69de29bb..000000000