From: Ben Darnell Date: Sun, 20 May 2018 00:44:56 +0000 (-0400) Subject: web: Also return a Future from render() X-Git-Tag: v5.1.0b1~12^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F2394%2Fhead;p=thirdparty%2Ftornado.git web: Also return a Future from render() Improve tests. --- diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 7d8d069a3..b77311df0 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -191,19 +191,38 @@ class SecureCookieV2Test(unittest.TestCase): self.assertEqual(new_handler.get_secure_cookie('foo'), None) -class FinishTest(WebTestCase): +class FinalReturnTest(WebTestCase): def get_handlers(self): + test = self + class FinishHandler(RequestHandler): + @gen.coroutine def get(self): - self.write("The finish method should return Future.") - f = self.finish() - assert isinstance(f, Future) + test.final_return = self.finish() - return [("/finish", FinishHandler)] + class RenderHandler(RequestHandler): + def create_template_loader(self, path): + return DictLoader({'foo.html': 'hi'}) + + @gen.coroutine + def get(self): + test.final_return = self.render('foo.html') + + return [("/finish", FinishHandler), + ("/render", RenderHandler)] + + def get_app_kwargs(self): + return dict(template_path='FinalReturnTest') def test_finish_method_return_future(self): response = self.fetch(self.get_url('/finish')) self.assertEqual(response.code, 200) + self.assertIsInstance(self.final_return, Future) + + def test_render_method_return_future(self): + response = self.fetch(self.get_url('/render')) + self.assertEqual(response.code, 200) + self.assertIsInstance(self.final_return, Future) class CookieTest(WebTestCase): diff --git a/tornado/web.py b/tornado/web.py index 3ce507f3d..6760b0b9a 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -749,7 +749,18 @@ class RequestHandler(object): self._write_buffer.append(chunk) def render(self, template_name, **kwargs): - """Renders the template with the given arguments as the response.""" + """Renders the template with the given arguments as the response. + + ``render()`` calls ``finish()``, so no other output methods can be called + after it. + + Returns a `.Future` with the same semantics as the one returned by `finish`. + Awaiting this `.Future` is optional. + + .. versionchanged:: 5.1 + + Now returns a `.Future` instead of ``None``. + """ if self._finished: raise RuntimeError("Cannot render() after finish()") html = self.render_string(template_name, **kwargs) @@ -810,7 +821,7 @@ class RequestHandler(object): if html_bodies: hloc = html.index(b'') html = html[:hloc] + b''.join(html_bodies) + b'\n' + html[hloc:] - self.finish(html) + return self.finish(html) def render_linked_js(self, js_files): """Default method used to render the final js links for the @@ -993,7 +1004,20 @@ class RequestHandler(object): return future def finish(self, chunk=None): - """Finishes this response, ending the HTTP request.""" + """Finishes this response, ending the HTTP request. + + Passing a ``chunk`` to ``finish()`` is equivalent to passing that + chunk to ``write()`` and then calling ``finish()`` with no arguments. + + Returns a `.Future` which may optionally be awaited to track the sending + of the response to the client. This `.Future` resolves when all the response + data has been sent, and raises an error if the connection is closed before all + data can be sent. + + .. versionchanged:: 5.1 + + Now returns a `.Future` instead of ``None``. + """ if self._finished: raise RuntimeError("finish() called twice")