Skip to content

bpo-36921: Deprecate @coroutine for sake of async def #13346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 16, 2019
7 changes: 4 additions & 3 deletions Doc/library/asyncio-task.rst
Original file line number Diff line number Diff line change
Expand Up @@ -916,12 +916,13 @@ enforced.
async def main():
await old_style_coroutine()

This decorator is **deprecated** and is scheduled for removal in
Python 3.10.

This decorator should not be used for :keyword:`async def`
coroutines.

.. deprecated-removed:: 3.8 3.10
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace free-form deprecation text with more standardized one.


Use :keyword:`async def` instead.

.. function:: iscoroutine(obj)

Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`.
Expand Down
4 changes: 4 additions & 0 deletions Lib/asyncio/coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import traceback
import types
import warnings

from . import base_futures
from . import constants
Expand Down Expand Up @@ -107,6 +108,9 @@ def coroutine(func):
If the coroutine is not yielded from before it is destroyed,
an error message is logged.
"""
warnings.warn('"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead',
DeprecationWarning,
stacklevel=2)
if inspect.iscoroutinefunction(func):
# In Python 3.5 that's all we need to do for coroutines
# defined with "async def".
Expand Down
8 changes: 6 additions & 2 deletions Lib/asyncio/locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
__all__ = ('Lock', 'Event', 'Condition', 'Semaphore', 'BoundedSemaphore')

import collections
import types
import warnings

from . import events
from . import futures
from . import exceptions
from .coroutines import coroutine
from .import coroutines


class _ContextManager:
Expand Down Expand Up @@ -55,7 +56,7 @@ def __exit__(self, *args):
# always raises; that's how the with-statement works.
pass

@coroutine
@types.coroutine
def __iter__(self):
# This is not a coroutine. It is meant to enable the idiom:
#
Expand All @@ -78,6 +79,9 @@ def __iter__(self):
yield from self.acquire()
return _ContextManager(self)

# The flag is needed for legacy asyncio.iscoroutine()
__iter__._is_coroutine = coroutines._is_coroutine

async def __acquire_ctx(self):
await self.acquire()
return _ContextManager(self)
Expand Down
6 changes: 4 additions & 2 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from . import events
from . import exceptions
from . import futures
from .coroutines import coroutine
from .coroutines import _is_coroutine

# Helper to generate new task names
# This uses itertools.count() instead of a "+= 1" operation because the latter
Expand Down Expand Up @@ -638,7 +638,7 @@ def ensure_future(coro_or_future, *, loop=None):
'required')


@coroutine
@types.coroutine
def _wrap_awaitable(awaitable):
"""Helper for asyncio.ensure_future().

Expand All @@ -647,6 +647,8 @@ def _wrap_awaitable(awaitable):
"""
return (yield from awaitable.__await__())

_wrap_awaitable._is_coroutine = _is_coroutine


class _GatheringFuture(futures.Future):
"""Helper for gather().
Expand Down
54 changes: 20 additions & 34 deletions Lib/test/test_asyncio/test_base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,9 +575,8 @@ def zero_error(fut):
def test_default_exc_handler_coro(self):
self.loop._process_events = mock.Mock()

@asyncio.coroutine
def zero_error_coro():
yield from asyncio.sleep(0.01)
async def zero_error_coro():
await asyncio.sleep(0.01)
1/0

# Test Future.__del__
Expand Down Expand Up @@ -723,8 +722,7 @@ def test_set_task_factory(self):
class MyTask(asyncio.Task):
pass

@asyncio.coroutine
def coro():
async def coro():
pass

factory = lambda loop, coro: MyTask(coro, loop=loop)
Expand Down Expand Up @@ -779,8 +777,7 @@ def test_create_task(self):
class MyTask(asyncio.Task):
pass

@asyncio.coroutine
def test():
async def test():
pass

class EventLoop(base_events.BaseEventLoop):
Expand Down Expand Up @@ -830,8 +827,7 @@ def test_run_forever_keyboard_interrupt(self):
# Python issue #22601: ensure that the temporary task created by
# run_forever() consumes the KeyboardInterrupt and so don't log
# a warning
@asyncio.coroutine
def raise_keyboard_interrupt():
async def raise_keyboard_interrupt():
raise KeyboardInterrupt

self.loop._process_events = mock.Mock()
Expand All @@ -849,8 +845,7 @@ def raise_keyboard_interrupt():
def test_run_until_complete_baseexception(self):
# Python issue #22429: run_until_complete() must not schedule a pending
# call to stop() if the future raised a BaseException
@asyncio.coroutine
def raise_keyboard_interrupt():
async def raise_keyboard_interrupt():
raise KeyboardInterrupt

self.loop._process_events = mock.Mock()
Expand Down Expand Up @@ -1070,9 +1065,7 @@ def test_create_connection_multiple_errors(self, m_socket):
class MyProto(asyncio.Protocol):
pass

@asyncio.coroutine
def getaddrinfo(*args, **kw):
yield from []
async def getaddrinfo(*args, **kw):
return [(2, 1, 6, '', ('107.6.106.82', 80)),
(2, 1, 6, '', ('107.6.106.82', 80))]

Expand Down Expand Up @@ -1191,9 +1184,8 @@ def test_create_connection_no_host_port_sock(self):
self.assertRaises(ValueError, self.loop.run_until_complete, coro)

def test_create_connection_no_getaddrinfo(self):
@asyncio.coroutine
def getaddrinfo(*args, **kw):
yield from []
async def getaddrinfo(*args, **kw):
return []

def getaddrinfo_task(*args, **kwds):
return asyncio.Task(getaddrinfo(*args, **kwds), loop=self.loop)
Expand All @@ -1219,8 +1211,7 @@ def getaddrinfo_task(*args, **kwds):
OSError, self.loop.run_until_complete, coro)

def test_create_connection_multiple(self):
@asyncio.coroutine
def getaddrinfo(*args, **kw):
async def getaddrinfo(*args, **kw):
return [(2, 1, 6, '', ('0.0.0.1', 80)),
(2, 1, 6, '', ('0.0.0.2', 80))]

Expand All @@ -1247,8 +1238,7 @@ def bind(addr):

m_socket.socket.return_value.bind = bind

@asyncio.coroutine
def getaddrinfo(*args, **kw):
async def getaddrinfo(*args, **kw):
return [(2, 1, 6, '', ('0.0.0.1', 80)),
(2, 1, 6, '', ('0.0.0.2', 80))]

Expand Down Expand Up @@ -1349,8 +1339,7 @@ def test_create_connection_service_name(self, m_socket):
self.loop.run_until_complete(coro)

def test_create_connection_no_local_addr(self):
@asyncio.coroutine
def getaddrinfo(host, *args, **kw):
async def getaddrinfo(host, *args, **kw):
if host == 'example.com':
return [(2, 1, 6, '', ('107.6.106.82', 80)),
(2, 1, 6, '', ('107.6.106.82', 80))]
Expand Down Expand Up @@ -1488,11 +1477,10 @@ def test_create_server_empty_host(self):
# if host is empty string use None instead
host = object()

@asyncio.coroutine
def getaddrinfo(*args, **kw):
async def getaddrinfo(*args, **kw):
nonlocal host
host = args[0]
yield from []
return []

def getaddrinfo_task(*args, **kwds):
return asyncio.Task(getaddrinfo(*args, **kwds), loop=self.loop)
Expand Down Expand Up @@ -1854,9 +1842,10 @@ def test_accept_connection_exception(self, m_log):
MyProto, sock, None, None, mock.ANY, mock.ANY)

def test_call_coroutine(self):
@asyncio.coroutine
def simple_coroutine():
pass
with self.assertWarns(DeprecationWarning):
@asyncio.coroutine
def simple_coroutine():
pass

self.loop.set_debug(True)
coro_func = simple_coroutine
Expand All @@ -1880,9 +1869,7 @@ def test_log_slow_callbacks(self, m_logger):
def stop_loop_cb(loop):
loop.stop()

@asyncio.coroutine
def stop_loop_coro(loop):
yield from ()
async def stop_loop_coro(loop):
loop.stop()

asyncio.set_event_loop(self.loop)
Expand All @@ -1909,8 +1896,7 @@ def stop_loop_coro(loop):
class RunningLoopTests(unittest.TestCase):

def test_running_loop_within_a_loop(self):
@asyncio.coroutine
def runner(loop):
async def runner(loop):
loop.run_forever()

loop = asyncio.new_event_loop()
Expand Down
20 changes: 8 additions & 12 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,10 @@ def tearDown(self):
super().tearDown()

def test_run_until_complete_nesting(self):
@asyncio.coroutine
def coro1():
yield
async def coro1():
await asyncio.sleep(0)

@asyncio.coroutine
def coro2():
async def coro2():
self.assertTrue(self.loop.is_running())
self.loop.run_until_complete(coro1())

Expand Down Expand Up @@ -735,8 +733,7 @@ def test_connect_accepted_socket_ssl_timeout_for_plain_socket(self):

@mock.patch('asyncio.base_events.socket')
def create_server_multiple_hosts(self, family, hosts, mock_sock):
@asyncio.coroutine
def getaddrinfo(host, port, *args, **kw):
async def getaddrinfo(host, port, *args, **kw):
if family == socket.AF_INET:
return [(family, socket.SOCK_STREAM, 6, '', (host, port))]
else:
Expand Down Expand Up @@ -1662,8 +1659,7 @@ def test_add_fds_after_closing(self):
loop.add_writer(w, callback)

def test_close_running_event_loop(self):
@asyncio.coroutine
def close_loop(loop):
async def close_loop(loop):
self.loop.close()

coro = close_loop(self.loop)
Expand All @@ -1673,8 +1669,7 @@ def close_loop(loop):
def test_close(self):
self.loop.close()

@asyncio.coroutine
def test():
async def test():
pass

func = lambda: False
Expand Down Expand Up @@ -2142,7 +2137,8 @@ def test_handle_repr(self):
'<Handle cancelled>')

# decorated function
cb = asyncio.coroutine(noop)
with self.assertWarns(DeprecationWarning):
cb = asyncio.coroutine(noop)
h = asyncio.Handle(cb, (), self.loop)
self.assertEqual(repr(h),
'<Handle noop() at %s:%s>'
Expand Down
Loading