Repository: tornadoweb/tornado
Pull request #2498 (2018-09-16 17:58:27)

branch5.1: Backport various changes
Mostly doc updates; the only non-doc changes are from #2449. 

Updates #2451 


Patch:
diff --git a/demos/blog/blog.py b/demos/blog/blog.py
index db43f09b..ee8e92c5 100755
--- a/demos/blog/blog.py
+++ b/demos/blog/blog.py
@@ -183,8 +183,9 @@ class ComposeHandler(BaseHandler):
         text = self.get_argument("markdown")
         html = markdown.markdown(text)
         if id:
-            entry = await self.query("SELECT * FROM entries WHERE id = %s", int(id))
-            if not entry:
+            try:
+                entry = await self.queryone("SELECT * FROM entries WHERE id = %s", int(id))
+            except NoResultError:
                 raise tornado.web.HTTPError(404)
             slug = entry.slug
             await self.execute(
diff --git a/docs/guide/coroutines.rst b/docs/guide/coroutines.rst
index 963c49fb..29f614cc 100644
--- a/docs/guide/coroutines.rst
+++ b/docs/guide/coroutines.rst
@@ -68,7 +68,7 @@ Other differences between the two forms of coroutine are:
   background" as soon as they are called. Note that for both kinds of
   coroutines it is important to use ``await`` or ``yield`` so that
   any exceptions have somewhere to go.
-- Decorated coroutines has additional integration with the
+- Decorated coroutines have additional integration with the
   `concurrent.futures` package, allowing the result of
   ``executor.submit`` to be yielded directly. For native coroutines,
   use `.IOLoop.run_in_executor` instead.
@@ -177,7 +177,7 @@ use `.IOLoop.run_in_executor`, which returns
 ``Futures`` that are compatible with coroutines::
 
     async def call_blocking():
-        await IOLoop.current().run_in_executor(blocking_func, args)
+        await IOLoop.current().run_in_executor(None, blocking_func, args)
 
 Parallelism
 ^^^^^^^^^^^
@@ -210,7 +210,7 @@ In decorated coroutines, it is possible to ``yield`` the list or dict directly::
     @gen.coroutine
     def parallel_fetch_decorated(url1, url2):
         resp1, resp2 = yield [http_client.fetch(url1),
-                              http_client.fetch(url2)])
+                              http_client.fetch(url2)]
 
 Interleaving
 ^^^^^^^^^^^^
diff --git a/docs/releases.rst b/docs/releases.rst
index 57525328..1b9e7e08 100644
--- a/docs/releases.rst
+++ b/docs/releases.rst
@@ -4,6 +4,7 @@ Release notes
 .. toctree::
    :maxdepth: 2
 
+   releases/v5.1.1
    releases/v5.1.0
    releases/v5.0.2
    releases/v5.0.1
diff --git a/docs/releases/v5.1.1.rst b/docs/releases/v5.1.1.rst
new file mode 100644
index 00000000..7fc4fb88
--- /dev/null
+++ b/docs/releases/v5.1.1.rst
@@ -0,0 +1,14 @@
+What's new in Tornado 5.1.1
+===========================
+
+Sep 16, 2018
+------------
+
+Bug fixes
+~~~~~~~~~
+
+- Fixed an case in which the `.Future` returned by
+  `.RequestHandler.finish` could fail to resolve.
+- The `.TwitterMixin.authenticate_redirect` method works again.
+- Improved error handling in the `tornado.auth` module, fixing hanging
+  requests when a network or other error occurs.
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..0c9e0fc1
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[metadata]
+license_file = LICENSE
diff --git a/setup.py b/setup.py
index ea1827f3..9127c6a6 100644
--- a/setup.py
+++ b/setup.py
@@ -103,7 +103,7 @@ MacOS users should run:
 
 kwargs = {}
 
-version = "5.1"
+version = "5.1.1 "
 
 with open('README.rst') as f:
     kwargs['long_description'] = f.read()
diff --git a/tornado/__init__.py b/tornado/__init__.py
index 6bb5ec20..b269cf70 100644
--- a/tornado/__init__.py
+++ b/tornado/__init__.py
@@ -24,5 +24,5 @@ from __future__ import absolute_import, division, print_function
 # is zero for an official release, positive for a development branch,
 # or negative for a release candidate or beta (after the base version
 # number has been incremented)
-version = "5.1"
-version_info = (5, 1, 0, 0)
+version = "5.1.1"
+version_info = (5, 1, 1, 0)
diff --git a/tornado/http1connection.py b/tornado/http1connection.py
index af7abe7c..6cc4071c 100644
--- a/tornado/http1connection.py
+++ b/tornado/http1connection.py
@@ -411,7 +411,7 @@ class HTTP1Connection(httputil.HTTPConnection):
             if chunk:
                 data += self._format_chunk(chunk)
             self._pending_write = self.stream.write(data)
-            self._pending_write.add_done_callback(self._on_write_complete)
+            future_add_done_callback(self._pending_write, self._on_write_complete)
         return future
 
     def _format_chunk(self, chunk):
diff --git a/tornado/ioloop.py b/tornado/ioloop.py
index f7ee6dd6..889153af 100644
--- a/tornado/ioloop.py
+++ b/tornado/ioloop.py
@@ -101,7 +101,6 @@ class IOLoop(Configurable):
         import socket
 
         import tornado.ioloop
-        from tornado import gen
         from tornado.iostream import IOStream
 
         async def handle_connection(connection, address):
@@ -118,7 +117,8 @@ class IOLoop(Configurable):
                         raise
                     return
                 connection.setblocking(0)
-                handle_connection(connection, address)
+                io_loop = tornado.ioloop.IOLoop.current()
+                io_loop.spawn_callback(handle_connection, connection, address)
 
         if __name__ == '__main__':
             sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)


Test patch:
diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
index 9a435c30..16938c42 100644
--- a/tornado/test/web_test.py
+++ b/tornado/test/web_test.py
@@ -199,6 +199,7 @@ class FinalReturnTest(WebTestCase):
             @gen.coroutine
             def get(self):
                 test.final_return = self.finish()
+                yield test.final_return
 
         class RenderHandler(RequestHandler):
             def create_template_loader(self, path):
@@ -218,6 +219,7 @@ class FinalReturnTest(WebTestCase):
         response = self.fetch(self.get_url('/finish'))
         self.assertEqual(response.code, 200)
         self.assertIsInstance(self.final_return, Future)
+        self.assertTrue(self.final_return.done())
 
     def test_render_method_return_future(self):
         response = self.fetch(self.get_url('/render'))