]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
gen: Deprecate gen.engine
authorBen Darnell <ben@bendarnell.com>
Sun, 18 Mar 2018 22:56:29 +0000 (18:56 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 18 Mar 2018 22:56:29 +0000 (18:56 -0400)
tornado/gen.py
tornado/test/auth_test.py
tornado/test/concurrent_test.py
tornado/test/gen_test.py
tornado/test/stack_context_test.py
tornado/test/web_test.py

index ef70374a70dbd69db163dc992b606d925a46c72f..d252c5a072dd5ee02d4cdbcebbf07cbd51b90ec1 100644 (file)
@@ -96,6 +96,7 @@ import itertools
 import os
 import sys
 import types
+import warnings
 
 from tornado.concurrent import (Future, is_future, chain_future, future_set_exc_info,
                                 future_add_done_callback, future_set_result_unless_cancelled)
@@ -213,7 +214,14 @@ def engine(func):
     they are finished.  One notable exception is the
     `~tornado.web.RequestHandler` :ref:`HTTP verb methods <verbs>`,
     which use ``self.finish()`` in place of a callback argument.
+
+    .. deprecated:: 5.1
+
+       This decorator will be removed in 6.0. Use `coroutine` or
+       ``async def`` instead.
     """
+    warnings.warn("gen.engine is deprecated, use gen.coroutine or async def instead",
+                  DeprecationWarning)
     func = _make_coroutine_wrapper(func, replace_callback=False)
 
     @functools.wraps(func)
index 704ab6c0659814a13af91102a9f1df0d397f5eb3..0d26a35afc92fcfb627da52a66266ecf78b2689c 100644 (file)
@@ -252,16 +252,17 @@ class TwitterClientLoginHandler(TwitterClientHandler):
 
 
 class TwitterClientLoginGenEngineHandler(TwitterClientHandler):
-    @asynchronous
-    @gen.engine
-    def get(self):
-        if self.get_argument("oauth_token", None):
-            user = yield self.get_authenticated_user()
-            self.finish(user)
-        else:
-            # Old style: with @gen.engine we can ignore the Future from
-            # authorize_redirect.
-            self.authorize_redirect()
+    with ignore_deprecation():
+        @asynchronous
+        @gen.engine
+        def get(self):
+            if self.get_argument("oauth_token", None):
+                user = yield self.get_authenticated_user()
+                self.finish(user)
+            else:
+                # Old style: with @gen.engine we can ignore the Future from
+                # authorize_redirect.
+                self.authorize_redirect()
 
 
 class TwitterClientLoginGenCoroutineHandler(TwitterClientHandler):
@@ -277,21 +278,22 @@ class TwitterClientLoginGenCoroutineHandler(TwitterClientHandler):
 
 
 class TwitterClientShowUserHandlerLegacy(TwitterClientHandler):
-    @asynchronous
-    @gen.engine
-    def get(self):
-        # TODO: would be nice to go through the login flow instead of
-        # cheating with a hard-coded access token.
-        with warnings.catch_warnings():
-            warnings.simplefilter('ignore', DeprecationWarning)
-            response = yield gen.Task(self.twitter_request,
-                                      '/users/show/%s' % self.get_argument('name'),
-                                      access_token=dict(key='hjkl', secret='vbnm'))
-        if response is None:
-            self.set_status(500)
-            self.finish('error from twitter request')
-        else:
-            self.finish(response)
+    with ignore_deprecation():
+        @asynchronous
+        @gen.engine
+        def get(self):
+            # TODO: would be nice to go through the login flow instead of
+            # cheating with a hard-coded access token.
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                response = yield gen.Task(self.twitter_request,
+                                          '/users/show/%s' % self.get_argument('name'),
+                                          access_token=dict(key='hjkl', secret='vbnm'))
+            if response is None:
+                self.set_status(500)
+                self.finish('error from twitter request')
+            else:
+                self.finish(response)
 
 
 class TwitterClientShowUserHandler(TwitterClientHandler):
@@ -310,22 +312,6 @@ class TwitterClientShowUserHandler(TwitterClientHandler):
             self.finish(response)
 
 
-class TwitterClientShowUserFutureHandler(TwitterClientHandler):
-    @asynchronous
-    @gen.engine
-    def get(self):
-        try:
-            response = yield self.twitter_request(
-                '/users/show/%s' % self.get_argument('name'),
-                access_token=dict(key='hjkl', secret='vbnm'))
-        except AuthError as e:
-            self.set_status(500)
-            self.finish(str(e))
-            return
-        assert response is not None
-        self.finish(response)
-
-
 class TwitterServerAccessTokenHandler(RequestHandler):
     def get(self):
         self.write('oauth_token=hjkl&oauth_token_secret=vbnm&screen_name=foo')
@@ -395,8 +381,6 @@ class AuthTest(AsyncHTTPTestCase):
                  TwitterClientShowUserHandlerLegacy, dict(test=self)),
                 ('/twitter/client/show_user',
                  TwitterClientShowUserHandler, dict(test=self)),
-                ('/twitter/client/show_user_future',
-                 TwitterClientShowUserFutureHandler, dict(test=self)),
 
                 # simulated servers
                 ('/openid/server/authenticate', OpenIdServerAuthenticateHandler),
@@ -623,17 +607,6 @@ class AuthTest(AsyncHTTPTestCase):
         self.assertEqual(response.code, 500)
         self.assertEqual(response.body, b'error from twitter request')
 
-    def test_twitter_show_user_future(self):
-        response = self.fetch('/twitter/client/show_user_future?name=somebody')
-        response.rethrow()
-        self.assertEqual(json_decode(response.body),
-                         {'name': 'Somebody', 'screen_name': 'somebody'})
-
-    def test_twitter_show_user_future_error(self):
-        response = self.fetch('/twitter/client/show_user_future?name=error')
-        self.assertEqual(response.code, 500)
-        self.assertIn(b'Error response HTTP 500', response.body)
-
 
 class GoogleLoginHandler(RequestHandler, GoogleOAuth2Mixin):
     def initialize(self, test):
index dca66547e3e9efdb6e8614bde104f4c9a7d2dca0..5199033a3475c8fa053c378cc8b97b8fffce5bc0 100644 (file)
@@ -177,11 +177,30 @@ class ReturnFutureTest(AsyncTestCase):
         self.assertIs(result, None)
         future.result()
 
+    @gen_test
+    def test_future_traceback_legacy(self):
+        with ignore_deprecation():
+            @return_future
+            @gen.engine
+            def f(callback):
+                yield gen.Task(self.io_loop.add_callback)
+                try:
+                    1 / 0
+                except ZeroDivisionError:
+                    self.expected_frame = traceback.extract_tb(
+                        sys.exc_info()[2], limit=1)[0]
+                    raise
+            try:
+                yield f()
+                self.fail("didn't get expected exception")
+            except ZeroDivisionError:
+                tb = traceback.extract_tb(sys.exc_info()[2])
+                self.assertIn(self.expected_frame, tb)
+
     @gen_test
     def test_future_traceback(self):
-        @return_future
-        @gen.engine
-        def f(callback):
+        @gen.coroutine
+        def f():
             yield gen.Task(self.io_loop.add_callback)
             try:
                 1 / 0
@@ -311,9 +330,8 @@ class DecoratorCapClient(BaseCapClient):
 
 
 class GeneratorCapClient(BaseCapClient):
-    @return_future
-    @gen.engine
-    def capitalize(self, request_data, callback):
+    @gen.coroutine
+    def capitalize(self, request_data):
         logging.debug('capitalize')
         stream = IOStream(socket.socket())
         logging.debug('connecting')
@@ -323,7 +341,7 @@ class GeneratorCapClient(BaseCapClient):
         data = yield gen.Task(stream.read_until, b'\n')
         logging.debug('returning')
         stream.close()
-        callback(self.process_response(data))
+        raise gen.Return(self.process_response(data))
 
 
 class ClientTestMixin(object):
@@ -362,22 +380,18 @@ class ClientTestMixin(object):
         self.assertRaisesRegexp(CapError, "already capitalized", future.result)
 
     def test_generator(self):
-        @gen.engine
+        @gen.coroutine
         def f():
             result = yield self.client.capitalize("hello")
             self.assertEqual(result, "HELLO")
-            self.stop()
-        f()
-        self.wait()
+        self.io_loop.run_sync(f)
 
     def test_generator_error(self):
-        @gen.engine
+        @gen.coroutine
         def f():
             with self.assertRaisesRegexp(CapError, "already capitalized"):
                 yield self.client.capitalize("HELLO")
-            self.stop()
-        f()
-        self.wait()
+        self.io_loop.run_sync(f)
 
 
 class ManualClientTest(ClientTestMixin, AsyncTestCase):
index 12621ceccb4d99359ddac9e00010ba49974af538..6c75b5a255434e698c740dffbdd2af720028dca3 100644 (file)
@@ -9,6 +9,7 @@ import sys
 import textwrap
 import time
 import weakref
+import warnings
 
 from tornado.concurrent import return_future, Future
 from tornado.escape import url_escape
@@ -17,7 +18,7 @@ from tornado.ioloop import IOLoop
 from tornado.log import app_log
 from tornado import stack_context
 from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test
-from tornado.test.util import unittest, skipOnTravis, skipBefore33, skipBefore35, skipNotCPython, exec_test  # noqa: E501
+from tornado.test.util import unittest, skipOnTravis, skipBefore33, skipBefore35, skipNotCPython, exec_test, ignore_deprecation  # noqa: E501
 from tornado.web import Application, RequestHandler, asynchronous, HTTPError
 
 from tornado import gen
@@ -35,9 +36,16 @@ except ImportError:
 
 class GenEngineTest(AsyncTestCase):
     def setUp(self):
+        self.warning_catcher = warnings.catch_warnings()
+        self.warning_catcher.__enter__()
+        warnings.simplefilter('ignore', DeprecationWarning)
         super(GenEngineTest, self).setUp()
         self.named_contexts = []
 
+    def tearDown(self):
+        super(GenEngineTest, self).tearDown()
+        self.warning_catcher.__exit__(None, None, None)
+
     def named_context(self, name):
         @contextlib.contextmanager
         def context():
@@ -1085,20 +1093,21 @@ class GenCoroutineTest(AsyncTestCase):
 
 
 class GenSequenceHandler(RequestHandler):
-    @asynchronous
-    @gen.engine
-    def get(self):
-        self.io_loop = self.request.connection.stream.io_loop
-        self.io_loop.add_callback((yield gen.Callback("k1")))
-        yield gen.Wait("k1")
-        self.write("1")
-        self.io_loop.add_callback((yield gen.Callback("k2")))
-        yield gen.Wait("k2")
-        self.write("2")
-        # reuse an old key
-        self.io_loop.add_callback((yield gen.Callback("k1")))
-        yield gen.Wait("k1")
-        self.finish("3")
+    with ignore_deprecation():
+        @asynchronous
+        @gen.engine
+        def get(self):
+            self.io_loop = self.request.connection.stream.io_loop
+            self.io_loop.add_callback((yield gen.Callback("k1")))
+            yield gen.Wait("k1")
+            self.write("1")
+            self.io_loop.add_callback((yield gen.Callback("k2")))
+            yield gen.Wait("k2")
+            self.write("2")
+            # reuse an old key
+            self.io_loop.add_callback((yield gen.Callback("k1")))
+            yield gen.Wait("k1")
+            self.finish("3")
 
 
 class GenCoroutineSequenceHandler(RequestHandler):
@@ -1136,8 +1145,7 @@ class GenCoroutineUnfinishedSequenceHandler(RequestHandler):
 
 
 class GenTaskHandler(RequestHandler):
-    @asynchronous
-    @gen.engine
+    @gen.coroutine
     def get(self):
         client = AsyncHTTPClient()
         response = yield gen.Task(client.fetch, self.get_argument('url'))
@@ -1146,13 +1154,14 @@ class GenTaskHandler(RequestHandler):
 
 
 class GenExceptionHandler(RequestHandler):
-    @asynchronous
-    @gen.engine
-    def get(self):
-        # This test depends on the order of the two decorators.
-        io_loop = self.request.connection.stream.io_loop
-        yield gen.Task(io_loop.add_callback)
-        raise Exception("oops")
+    with ignore_deprecation():
+        @asynchronous
+        @gen.engine
+        def get(self):
+            # This test depends on the order of the two decorators.
+            io_loop = self.request.connection.stream.io_loop
+            yield gen.Task(io_loop.add_callback)
+            raise Exception("oops")
 
 
 class GenCoroutineExceptionHandler(RequestHandler):
@@ -1165,8 +1174,7 @@ class GenCoroutineExceptionHandler(RequestHandler):
 
 
 class GenYieldExceptionHandler(RequestHandler):
-    @asynchronous
-    @gen.engine
+    @gen.coroutine
     def get(self):
         io_loop = self.request.connection.stream.io_loop
         # Test the interaction of the two stack_contexts.
index 098f0f0f0cd6fa071cfc00830cbb61a1ef68c82e..7ddd818a56c08b256537913d1d4cebe22ccb1b6c 100644 (file)
@@ -6,7 +6,7 @@ from tornado.log import app_log
 from tornado.stack_context import (StackContext, wrap, NullContext, StackContextInconsistentError,
                                    ExceptionStackContext, run_with_stack_context, _state)
 from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test
-from tornado.test.util import unittest
+from tornado.test.util import unittest, ignore_deprecation
 from tornado.web import asynchronous, Application, RequestHandler
 import contextlib
 import functools
@@ -215,15 +215,16 @@ class StackContextTest(AsyncTestCase):
         self.wait()
 
     def test_yield_in_with(self):
-        @gen.engine
-        def f():
-            self.callback = yield gen.Callback('a')
-            with StackContext(functools.partial(self.context, 'c1')):
-                # This yield is a problem: the generator will be suspended
-                # and the StackContext's __exit__ is not called yet, so
-                # the context will be left on _state.contexts for anything
-                # that runs before the yield resolves.
-                yield gen.Wait('a')
+        with ignore_deprecation():
+            @gen.engine
+            def f():
+                self.callback = yield gen.Callback('a')
+                with StackContext(functools.partial(self.context, 'c1')):
+                    # This yield is a problem: the generator will be suspended
+                    # and the StackContext's __exit__ is not called yet, so
+                    # the context will be left on _state.contexts for anything
+                    # that runs before the yield resolves.
+                    yield gen.Wait('a')
 
         with self.assertRaises(StackContextInconsistentError):
             f()
@@ -244,14 +245,15 @@ class StackContextTest(AsyncTestCase):
 
     def test_yield_in_with_exception_stack_context(self):
         # As above, but with ExceptionStackContext instead of StackContext.
-        @gen.engine
-        def f():
-            with ExceptionStackContext(lambda t, v, tb: False):
-                yield gen.Task(self.io_loop.add_callback)
-
-        with self.assertRaises(StackContextInconsistentError):
-            f()
-            self.wait()
+        with ignore_deprecation():
+            @gen.engine
+            def f():
+                with ExceptionStackContext(lambda t, v, tb: False):
+                    yield gen.Task(self.io_loop.add_callback)
+
+            with self.assertRaises(StackContextInconsistentError):
+                f()
+                self.wait()
 
     @gen_test
     def test_yield_outside_with_exception_stack_context(self):
index 39347deb431971b5e09936640a8eaedad1298c19..6aa06266efe68b6d36bcb490b4364091f8e51b54 100644 (file)
@@ -582,8 +582,7 @@ class RedirectHandler(RequestHandler):
 
 
 class EmptyFlushCallbackHandler(RequestHandler):
-    @asynchronous
-    @gen.engine
+    @gen.coroutine
     def get(self):
         # Ensure that the flush callback is run whether or not there
         # was any output.  The gen.Task and direct yield forms are