]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
ioloop: Redefine instance() in terms of current() 2063/head
authorBen Darnell <ben@bendarnell.com>
Sun, 28 May 2017 14:44:55 +0000 (10:44 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 28 May 2017 14:44:55 +0000 (10:44 -0400)
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().

docs/ioloop.rst
tornado/ioloop.py
tornado/testing.py

index afd2e2f065378b143daece5e2948c03437e68027..f4897d644b850997245bec841bd634b73b2554ec 100644 (file)
 
    .. 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
    ^^^^^^^^^^
index f4d4642077d45632d058db7d4ef6de44a7effbf5..46a60fb36d1896e1b1f814484f9fbeabce2b71fb 100644 (file)
@@ -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
index 9979617dc1bbc3d8e387e3868d09d415ce14ecfc..2f9b4a41e10e858c91a6f9e10e6962b7b1a7cf23 100644 (file)
@@ -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()