From: Ben Darnell Date: Sun, 28 May 2017 14:44:55 +0000 (-0400) Subject: ioloop: Redefine instance() in terms of current() X-Git-Tag: v5.0.0~77^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F2063%2Fhead;p=thirdparty%2Ftornado.git ioloop: Redefine instance() in terms of current() This aligns us more closely with asyncio, which only has a single get_event_loop() function. Most uses of instance() are simply a holdover from before current() was introduced and are not actually relying on its slightly different behavior, so we redefine it (and the related methods install(), initialized(), and clear_instance()) in terms of current(). --- diff --git a/docs/ioloop.rst b/docs/ioloop.rst index afd2e2f06..f4897d644 100644 --- a/docs/ioloop.rst +++ b/docs/ioloop.rst @@ -13,14 +13,15 @@ .. automethod:: IOLoop.current .. automethod:: IOLoop.make_current - .. automethod:: IOLoop.instance - .. automethod:: IOLoop.initialized - .. automethod:: IOLoop.install - .. automethod:: IOLoop.clear_instance + .. automethod:: IOLoop.clear_current .. automethod:: IOLoop.start .. automethod:: IOLoop.stop .. automethod:: IOLoop.run_sync .. automethod:: IOLoop.close + .. automethod:: IOLoop.instance + .. automethod:: IOLoop.initialized + .. automethod:: IOLoop.install + .. automethod:: IOLoop.clear_instance I/O events ^^^^^^^^^^ diff --git a/tornado/ioloop.py b/tornado/ioloop.py index f4d464207..46a60fb36 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -158,47 +158,70 @@ class IOLoop(Configurable): @staticmethod def instance(): - """Returns a global `IOLoop` instance. - - Most applications have a single, global `IOLoop` running on the - main thread. Use this method to get this instance from - another thread. In most other cases, it is better to use `current()` - to get the current thread's `IOLoop`. + """Deprecated alias for `IOLoop.current()`. + + .. versionchanged:: 5.0 + + Previously, this method returned a global singleton + `IOLoop`, in contrast with the per-thread `IOLoop` returned + by `current()`. In nearly all cases the two were the same + (when they differed, it was generally used from non-Tornado + threads to communicate back to the main thread's `IOLoop`). + This distinction is not present in `asyncio`, so in order + to facilitate integration with that package `instance()` + was changed to be an alias to `current()`. Applications + using the cross-thread communications aspect of + `instance()` should instead set their own global variable + to point to the `IOLoop` they want to use. + + .. deprecated:: 5.0 """ - if not hasattr(IOLoop, "_instance"): - with IOLoop._instance_lock: - if not hasattr(IOLoop, "_instance"): - # New instance after double check - IOLoop._instance = IOLoop() - return IOLoop._instance + return IOLoop.current() @staticmethod def initialized(): - """Returns true if the singleton instance has been created.""" - return hasattr(IOLoop, "_instance") + """Returns true if there is a current IOLoop. + + .. versionchanged:: 5.0 + + Redefined in terms of `current()` instead of `instance()`. + + .. deprecated:: 5.0 + + This method only knows about `IOLoop` objects (and not, for + example, `asyncio` event loops), so it is of limited use. + """ + return IOLoop.current(instance=False) is not None def install(self): - """Installs this `IOLoop` object as the singleton instance. + """Deprecated alias for `make_current()`. + + .. versionchanged:: 5.0 - This is normally not necessary as `instance()` will create - an `IOLoop` on demand, but you may want to call `install` to use - a custom subclass of `IOLoop`. + Previously, this method would set this `IOLoop` as the + global singleton used by `IOLoop.instance()`. Now that + `instance()` is an alias for `current()`, `install()` + is an alias for `make_current()`. - When using an `IOLoop` subclass, `install` must be called prior - to creating any objects that implicitly create their own - `IOLoop` (e.g., :class:`tornado.httpclient.AsyncHTTPClient`). + .. deprecated:: 5.0 """ - assert not IOLoop.initialized() - IOLoop._instance = self + self.make_current() @staticmethod def clear_instance(): - """Clear the global `IOLoop` instance. + """Deprecated alias for `clear_current()`. + + .. versionchanged:: 5.0 + + Previously, this method would clear the `IOLoop` used as + the global singleton by `IOLoop.instance()`. Now that + `instance()` is an alias for `current()`, + `clear_instance()` is an alias for `clear_instance()`. + + .. deprecated:: 5.0 - .. versionadded:: 4.0 """ - if hasattr(IOLoop, "_instance"): - del IOLoop._instance + IOLoop.clear_current() @staticmethod def current(instance=True): @@ -206,22 +229,22 @@ class IOLoop(Configurable): If an `IOLoop` is currently running or has been marked as current by `make_current`, returns that instance. If there is - no current `IOLoop`, returns `IOLoop.instance()` (i.e. the - main thread's `IOLoop`, creating one if necessary) if ``instance`` - is true. - - In general you should use `IOLoop.current` as the default when - constructing an asynchronous object, and use `IOLoop.instance` - when you mean to communicate to the main thread from a different - one. + no current `IOLoop` and ``instance`` is true, creates one. .. versionchanged:: 4.1 Added ``instance`` argument to control the fallback to `IOLoop.instance()`. + .. versionchanged:: 5.0 + The ``instance`` argument now controls whether an `IOLoop` + is created automatically when there is none, instead of + whether we fall back to `IOLoop.instance()` (which is now + an alias for this method) """ current = getattr(IOLoop._current, "instance", None) if current is None and instance: - return IOLoop.instance() + current = IOLoop() + if IOLoop._current.instance is not current: + raise RuntimeError("new IOLoop did not become current") return current def make_current(self): @@ -241,6 +264,10 @@ class IOLoop(Configurable): @staticmethod def clear_current(): + """Clears the `IOLoop` for the current thread. + + Intended primarily for use by test frameworks in between tests. + """ IOLoop._current.instance = None @classmethod diff --git a/tornado/testing.py b/tornado/testing.py index 9979617dc..2f9b4a41e 100644 --- a/tornado/testing.py +++ b/tornado/testing.py @@ -216,13 +216,11 @@ class AsyncTestCase(unittest.TestCase): # Clean up Subprocess, so it can be used again with a new ioloop. Subprocess.uninitialize() self.io_loop.clear_current() - if (not IOLoop.initialized() or - self.io_loop is not IOLoop.instance()): - # Try to clean up any file descriptors left open in the ioloop. - # This avoids leaks, especially when tests are run repeatedly - # in the same process with autoreload (because curl does not - # set FD_CLOEXEC on its file descriptors) - self.io_loop.close(all_fds=True) + # Try to clean up any file descriptors left open in the ioloop. + # This avoids leaks, especially when tests are run repeatedly + # in the same process with autoreload (because curl does not + # set FD_CLOEXEC on its file descriptors) + self.io_loop.close(all_fds=True) super(AsyncTestCase, self).tearDown() # In case an exception escaped or the StackContext caught an exception # when there wasn't a wait() to re-raise it, do so here. @@ -416,9 +414,7 @@ class AsyncHTTPTestCase(AsyncTestCase): self.http_server.stop() self.io_loop.run_sync(self.http_server.close_all_connections, timeout=get_async_test_timeout()) - if (not IOLoop.initialized() or - self.http_client.io_loop is not IOLoop.instance()): - self.http_client.close() + self.http_client.close() super(AsyncHTTPTestCase, self).tearDown()