@@ -84,39 +84,6 @@ def get_async_test_timeout() -> float:
8484 return 5
8585
8686
87- class _TestMethodWrapper (object ):
88- """Wraps a test method to raise an error if it returns a value.
89-
90- This is mainly used to detect undecorated generators (if a test
91- method yields it must use a decorator to consume the generator),
92- but will also detect other kinds of return values (these are not
93- necessarily errors, but we alert anyway since there is no good
94- reason to return a value from a test).
95- """
96-
97- def __init__ (self , orig_method : Callable ) -> None :
98- self .orig_method = orig_method
99- self .__wrapped__ = orig_method
100-
101- def __call__ (self , * args : Any , ** kwargs : Any ) -> None :
102- result = self .orig_method (* args , ** kwargs )
103- if isinstance (result , Generator ) or inspect .iscoroutine (result ):
104- raise TypeError (
105- "Generator and coroutine test methods should be"
106- " decorated with tornado.testing.gen_test"
107- )
108- elif result is not None :
109- raise ValueError ("Return value from test method ignored: %r" % result )
110-
111- def __getattr__ (self , name : str ) -> Any :
112- """Proxy all unknown attributes to the original method.
113-
114- This is important for some of the decorators in the `unittest`
115- module, such as `unittest.skipIf`.
116- """
117- return getattr (self .orig_method , name )
118-
119-
12087class AsyncTestCase (unittest .TestCase ):
12188 """`~unittest.TestCase` subclass for testing `.IOLoop`-based
12289 asynchronous code.
@@ -173,12 +140,6 @@ def __init__(self, methodName: str = "runTest") -> None:
173140 self .__stop_args = None # type: Any
174141 self .__timeout = None # type: Optional[object]
175142
176- # It's easy to forget the @gen_test decorator, but if you do
177- # the test will silently be ignored because nothing will consume
178- # the generator. Replace the test method with a wrapper that will
179- # make sure it's not an undecorated generator.
180- setattr (self , methodName , _TestMethodWrapper (getattr (self , methodName )))
181-
182143 # Not used in this class itself, but used by @gen_test
183144 self ._test_generator = None # type: Optional[Union[Generator, Coroutine]]
184145
@@ -289,6 +250,30 @@ def run(
289250 self .__rethrow ()
290251 return ret
291252
253+ def _callTestMethod (self , method : Callable ) -> None :
254+ """Run the given test method, raising an error if it returns non-None.
255+
256+ Failure to decorate asynchronous test methods with ``@gen_test`` can lead to tests
257+ incorrectly passing.
258+
259+ Remove this override when Python 3.10 support is dropped. This check (in the form of a
260+ DeprecationWarning) became a part of the standard library in 3.11.
261+
262+ Note that ``_callTestMethod`` is not documented as a public interface. However, it is
263+ present in all supported versions of Python (3.8+), and if it goes away in the future that's
264+ OK because we can just remove this override as noted above.
265+ """
266+ # Calling super()._callTestMethod would hide the return value, even in python 3.8-3.10
267+ # where the check isn't being done for us.
268+ result = method ()
269+ if isinstance (result , Generator ) or inspect .iscoroutine (result ):
270+ raise TypeError (
271+ "Generator and coroutine test methods should be"
272+ " decorated with tornado.testing.gen_test"
273+ )
274+ elif result is not None :
275+ raise ValueError ("Return value from test method ignored: %r" % result )
276+
292277 def stop (self , _arg : Any = None , ** kwargs : Any ) -> None :
293278 """Stops the `.IOLoop`, causing one pending (or future) call to `wait()`
294279 to return.
0 commit comments