From: Ben Darnell Date: Sun, 29 Mar 2015 20:22:26 +0000 (-0400) Subject: Add make_current keyword argument to IOLoop constructor. X-Git-Tag: v4.2.0b1~40 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4f86890cfad97ac9e3ef2c06bb313f7f4deb6d3b;p=thirdparty%2Ftornado.git Add make_current keyword argument to IOLoop constructor. This allows applications to create an IOLoop to be started in another thread without making it current in the originating thread. Update docs to use IOLoop.current() in place of IOLoop.instance() when starting the loop. Closes #1390. --- diff --git a/demos/benchmark/chunk_benchmark.py b/demos/benchmark/chunk_benchmark.py index 1502838ab..ba2e0648a 100755 --- a/demos/benchmark/chunk_benchmark.py +++ b/demos/benchmark/chunk_benchmark.py @@ -13,6 +13,7 @@ define('port', default=8888) define('num_chunks', default=1000) define('chunk_size', default=2048) + class ChunkHandler(RequestHandler): def get(self): for i in xrange(options.num_chunks): @@ -20,28 +21,30 @@ class ChunkHandler(RequestHandler): self.flush() self.finish() + def main(): parse_command_line() app = Application([('/', ChunkHandler)]) app.listen(options.port, address='127.0.0.1') + def callback(response): response.rethrow() assert len(response.body) == (options.num_chunks * options.chunk_size) logging.warning("fetch completed in %s seconds", response.request_time) - IOLoop.instance().stop() + IOLoop.current().stop() logging.warning("Starting fetch with curl client") curl_client = CurlAsyncHTTPClient() curl_client.fetch('http://localhost:%d/' % options.port, callback=callback) - IOLoop.instance().start() + IOLoop.current().start() logging.warning("Starting fetch with simple client") simple_client = SimpleAsyncHTTPClient() simple_client.fetch('http://localhost:%d/' % options.port, callback=callback) - IOLoop.instance().start() - + IOLoop.current().start() + if __name__ == '__main__': main() diff --git a/demos/blog/blog.py b/demos/blog/blog.py index 2707875cc..fde279e46 100755 --- a/demos/blog/blog.py +++ b/demos/blog/blog.py @@ -231,7 +231,7 @@ def main(): tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": diff --git a/demos/chat/chatdemo.py b/demos/chat/chatdemo.py index bd4d7b085..b4978bbd9 100755 --- a/demos/chat/chatdemo.py +++ b/demos/chat/chatdemo.py @@ -125,7 +125,7 @@ def main(): debug=options.debug, ) app.listen(options.port) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": diff --git a/demos/facebook/facebook.py b/demos/facebook/facebook.py index 5390a82f1..ace0705fc 100755 --- a/demos/facebook/facebook.py +++ b/demos/facebook/facebook.py @@ -116,7 +116,7 @@ def main(): return http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": diff --git a/demos/helloworld/helloworld.py b/demos/helloworld/helloworld.py index 0f1ed61ff..06eac9290 100755 --- a/demos/helloworld/helloworld.py +++ b/demos/helloworld/helloworld.py @@ -36,7 +36,7 @@ def main(): ]) http_server = tornado.httpserver.HTTPServer(application) http_server.listen(options.port) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": diff --git a/demos/s3server/s3server.py b/demos/s3server/s3server.py index 5a4805694..ff289f025 100644 --- a/demos/s3server/s3server.py +++ b/demos/s3server/s3server.py @@ -48,7 +48,7 @@ def start(port, root_directory="/tmp/s3", bucket_depth=0): application = S3Application(root_directory, bucket_depth) http_server = httpserver.HTTPServer(application) http_server.listen(port) - ioloop.IOLoop.instance().start() + ioloop.IOLoop.current().start() class S3Application(web.Application): diff --git a/demos/twitter/twitterdemo.py b/demos/twitter/twitterdemo.py index bf14c7967..73aa6ba3f 100644 --- a/demos/twitter/twitterdemo.py +++ b/demos/twitter/twitterdemo.py @@ -87,7 +87,7 @@ def main(): app.listen(options.port) logging.info('Listening on http://localhost:%d' % options.port) - IOLoop.instance().start() + IOLoop.current().start() if __name__ == '__main__': main() diff --git a/demos/websocket/chatdemo.py b/demos/websocket/chatdemo.py index ad5b2c2e1..aac24d94e 100755 --- a/demos/websocket/chatdemo.py +++ b/demos/websocket/chatdemo.py @@ -99,7 +99,7 @@ def main(): tornado.options.parse_command_line() app = Application() app.listen(options.port) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": diff --git a/docs/asyncio.rst b/docs/asyncio.rst index b9e7459cb..a68035034 100644 --- a/docs/asyncio.rst +++ b/docs/asyncio.rst @@ -41,7 +41,7 @@ loops. from tornado.ioloop import IOLoop IOLoop.configure('tornado.platform.asyncio.AsyncIOLoop') - IOLoop.instance().start() + IOLoop.current().start() Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object can be accessed with the ``asyncio_loop`` attribute. diff --git a/docs/index.rst b/docs/index.rst index 92dc18230..aae707710 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -46,7 +46,7 @@ Here is a simple "Hello, world" example web app for Tornado:: if __name__ == "__main__": application.listen(8888) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() This example does not use any of Tornado's asynchronous features; for that see this `simple chat room diff --git a/docs/twisted.rst b/docs/twisted.rst index 7709e769b..f60d08e5d 100644 --- a/docs/twisted.rst +++ b/docs/twisted.rst @@ -22,7 +22,7 @@ Twisted on Tornado tornado.platform.twisted.install() from twisted.internet import reactor - When the app is ready to start, call ``IOLoop.instance().start()`` + When the app is ready to start, call ``IOLoop.current().start()`` instead of ``reactor.run()``. It is also possible to create a non-global reactor by calling diff --git a/tornado/httpclient.py b/tornado/httpclient.py index c16563f63..c2e686236 100644 --- a/tornado/httpclient.py +++ b/tornado/httpclient.py @@ -72,7 +72,7 @@ class HTTPClient(object): http_client.close() """ def __init__(self, async_client_class=None, **kwargs): - self._io_loop = IOLoop() + self._io_loop = IOLoop(make_current=False) if async_client_class is None: async_client_class = AsyncHTTPClient self._async_client = async_client_class(self._io_loop, **kwargs) diff --git a/tornado/httpserver.py b/tornado/httpserver.py index 226f966a3..6062b7936 100644 --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -79,7 +79,7 @@ class HTTPServer(TCPServer, Configurable, server = HTTPServer(app) server.listen(8888) - IOLoop.instance().start() + IOLoop.current().start() In many cases, `tornado.web.Application.listen` can be used to avoid the need to explicitly create the `HTTPServer`. @@ -90,7 +90,7 @@ class HTTPServer(TCPServer, Configurable, server = HTTPServer(app) server.bind(8888) server.start(0) # Forks multiple sub-processes - IOLoop.instance().start() + IOLoop.current().start() When using this interface, an `.IOLoop` must *not* be passed to the `HTTPServer` constructor. `~.TCPServer.start` will always start @@ -102,7 +102,7 @@ class HTTPServer(TCPServer, Configurable, tornado.process.fork_processes(0) server = HTTPServer(app) server.add_sockets(sockets) - IOLoop.instance().start() + IOLoop.current().start() The `~.TCPServer.add_sockets` interface is more complicated, but it can be used with `tornado.process.fork_processes` to diff --git a/tornado/ioloop.py b/tornado/ioloop.py index f18d6985b..4a37a61cb 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -104,7 +104,7 @@ class IOLoop(Configurable): sock.bind(("", port)) sock.listen(128) - io_loop = tornado.ioloop.IOLoop.instance() + io_loop = tornado.ioloop.IOLoop.current() callback = functools.partial(connection_ready, sock) io_loop.add_handler(sock.fileno(), callback, io_loop.READ) io_loop.start() @@ -112,6 +112,17 @@ class IOLoop(Configurable): .. testoutput:: :hide: + By default, a newly-constructed `IOLoop` becomes the thread's current + `IOLoop`, unless there already is a current `IOLoop`. This behavior + can be controlled with the ``make_current`` argument to the `IOLoop` + constructor: if ``make_current=True``, the new `IOLoop` will always + try to become current and it raises an error if there is already a + current instance. If ``make_current=False``, the new `IOLoop` will + not try to become current. + + .. versionchanged:: 4.2 + Added the ``make_current`` keyword argument to the `IOLoop` + constructor. """ # Constants from the epoll module _EPOLLIN = 0x001 @@ -140,7 +151,8 @@ class IOLoop(Configurable): Most applications have a single, global `IOLoop` running on the main thread. Use this method to get this instance from - another thread. To get the current thread's `IOLoop`, use `current()`. + another thread. In most other cases, it is better to use `current()` + to get the current thread's `IOLoop`. """ if not hasattr(IOLoop, "_instance"): with IOLoop._instance_lock: @@ -232,8 +244,13 @@ class IOLoop(Configurable): from tornado.platform.select import SelectIOLoop return SelectIOLoop - def initialize(self): - if IOLoop.current(instance=False) is None: + def initialize(self, make_current=None): + if make_current is None: + if IOLoop.current(instance=False) is None: + self.make_current() + elif make_current: + if IOLoop.current(instance=False) is None: + raise RuntimeError("current IOLoop already exists") self.make_current() def close(self, all_fds=False): @@ -400,7 +417,7 @@ class IOLoop(Configurable): # do stuff... if __name__ == '__main__': - IOLoop.instance().run_sync(main) + IOLoop.current().run_sync(main) """ future_cell = [None] @@ -643,8 +660,8 @@ class PollIOLoop(IOLoop): (Linux), `tornado.platform.kqueue.KQueueIOLoop` (BSD and Mac), or `tornado.platform.select.SelectIOLoop` (all platforms). """ - def initialize(self, impl, time_func=None): - super(PollIOLoop, self).initialize() + def initialize(self, impl, time_func=None, **kwargs): + super(PollIOLoop, self).initialize(**kwargs) self._impl = impl if hasattr(self._impl, 'fileno'): set_close_exec(self._impl.fileno()) diff --git a/tornado/iostream.py b/tornado/iostream.py index a0f1eee41..3a175a679 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -971,13 +971,13 @@ class IOStream(BaseIOStream): def on_body(data): print(data) stream.close() - tornado.ioloop.IOLoop.instance().stop() + tornado.ioloop.IOLoop.current().stop() if __name__ == '__main__': s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) stream = tornado.iostream.IOStream(s) stream.connect(("friendfeed.com", 80), send_request) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() .. testoutput:: :hide: diff --git a/tornado/platform/twisted.py b/tornado/platform/twisted.py index 3d7eba422..86380ba13 100644 --- a/tornado/platform/twisted.py +++ b/tornado/platform/twisted.py @@ -35,7 +35,7 @@ of the application:: tornado.platform.twisted.install() from twisted.internet import reactor -When the app is ready to start, call `IOLoop.instance().start()` +When the app is ready to start, call `IOLoop.current().start()` instead of `reactor.run()`. It is also possible to create a non-global reactor by calling diff --git a/tornado/tcpserver.py b/tornado/tcpserver.py index da5ba7bbf..c9d148a80 100644 --- a/tornado/tcpserver.py +++ b/tornado/tcpserver.py @@ -57,14 +57,14 @@ class TCPServer(object): server = TCPServer() server.listen(8888) - IOLoop.instance().start() + IOLoop.current().start() 2. `bind`/`start`: simple multi-process:: server = TCPServer() server.bind(8888) server.start(0) # Forks multiple sub-processes - IOLoop.instance().start() + IOLoop.current().start() When using this interface, an `.IOLoop` must *not* be passed to the `TCPServer` constructor. `start` will always start @@ -76,7 +76,7 @@ class TCPServer(object): tornado.process.fork_processes(0) server = TCPServer() server.add_sockets(sockets) - IOLoop.instance().start() + IOLoop.current().start() The `add_sockets` interface is more complicated, but it can be used with `tornado.process.fork_processes` to give you more diff --git a/tornado/test/process_test.py b/tornado/test/process_test.py index 6abfabd28..72474167c 100644 --- a/tornado/test/process_test.py +++ b/tornado/test/process_test.py @@ -85,7 +85,7 @@ class ProcessTest(unittest.TestCase): self.assertEqual(id, task_id()) server = HTTPServer(self.get_app()) server.add_sockets([sock]) - IOLoop.instance().start() + IOLoop.current().start() elif id == 2: self.assertEqual(id, task_id()) sock.close() diff --git a/tornado/web.py b/tornado/web.py index 6b76e6742..93de85c32 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -35,7 +35,7 @@ Here is a simple "Hello, world" example app: (r"/", MainHandler), ]) application.listen(8888) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() .. testoutput:: :hide: @@ -1664,7 +1664,7 @@ class Application(httputil.HTTPServerConnectionDelegate): ]) http_server = httpserver.HTTPServer(application) http_server.listen(8080) - ioloop.IOLoop.instance().start() + ioloop.IOLoop.current().start() The constructor for this class takes in a list of `URLSpec` objects or (regexp, request_class) tuples. When we receive requests, we @@ -1762,7 +1762,7 @@ class Application(httputil.HTTPServerConnectionDelegate): `.TCPServer.bind`/`.TCPServer.start` methods directly. Note that after calling this method you still need to call - ``IOLoop.instance().start()`` to start the server. + ``IOLoop.current().start()`` to start the server. """ # import is here rather than top level because HTTPServer # is not importable on appengine diff --git a/tornado/wsgi.py b/tornado/wsgi.py index e08566afb..59e6c559f 100644 --- a/tornado/wsgi.py +++ b/tornado/wsgi.py @@ -253,7 +253,7 @@ class WSGIContainer(object): container = tornado.wsgi.WSGIContainer(simple_app) http_server = tornado.httpserver.HTTPServer(container) http_server.listen(8888) - tornado.ioloop.IOLoop.instance().start() + tornado.ioloop.IOLoop.current().start() This class is intended to let other frameworks (Django, web.py, etc) run on the Tornado HTTP server and I/O loop.