classification
Title: deprecate *loop* argument for asyncio.sleep
Type: enhancement Stage: resolved
Components: asyncio Versions: Python 3.8
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: asvetlov, fbidu, serhiy.storchaka, vstinner, willingc, xtreak, yselivanov
Priority: normal Keywords: easy, patch

Created on 2018-09-18 21:44 by yselivanov, last changed 2018-10-02 17:59 by yselivanov. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 9415 merged python-dev, 2018-09-19 10:29
PR 9661 merged yselivanov, 2018-10-01 22:34
Messages (12)
msg325684 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-09-18 21:44
asyncio.sleep is a coroutine; passing a *loop* argument to it makes no sense anymore.

We should raise a DeprecationWarning in Python 3.8 and 3.9 and remove it in 4.0.
msg325685 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-09-18 21:49
Same for asyncio.wait and asyncio.wait_for.
msg326213 - (view) Author: Carol Willing (willingc) * (Python committer) Date: 2018-09-24 09:51
New changeset 558c49bcf3a8543d64a68de836b5d855efd56696 by Carol Willing (João Júnior) in branch 'master':
bpo-34728: Remove deprecate *loop* argument in asyncio.sleep (GH-9415)
https://github.com/python/cpython/commit/558c49bcf3a8543d64a68de836b5d855efd56696
msg326825 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-10-01 20:40
Tests are failed when ran with -Werror.

$ ./python -Werror -m test -vuall test_asyncgen
...
======================================================================
ERROR: test_async_gen_asyncio_01 (test.test_asyncgen.AsyncGenAsyncioTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/serhiy/py/cpython/Lib/test/test_asyncgen.py", line 404, in test_async_gen_asyncio_01
    res = self.loop.run_until_complete(self.to_list(gen()))
  File "/home/serhiy/py/cpython/Lib/asyncio/base_events.py", line 582, in run_until_complete
    return future.result()
  File "/home/serhiy/py/cpython/Lib/test/test_asyncgen.py", line 391, in to_list
    async for i in gen:
  File "/home/serhiy/py/cpython/Lib/test/test_asyncgen.py", line 398, in gen
    await asyncio.sleep(0.01, loop=self.loop)
  File "/home/serhiy/py/cpython/Lib/asyncio/tasks.py", line 598, in sleep
    warnings.warn("The loop argument is deprecated and scheduled for "
DeprecationWarning: The loop argument is deprecated and scheduled for removal in Python 3.10.

======================================================================
...
(the full log is too long)

$ ./python -Werror -m test -vuall test_asyncio
...
======================================================================
FAIL: test_sleep_cancel (test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/serhiy/py/cpython/Lib/test/test_asyncio/utils.py", line 510, in close_loop
    loop.close()
  File "/home/serhiy/py/cpython/Lib/test/test_asyncio/utils.py", line 362, in close
    self._gen.send(0)
  File "/home/serhiy/py/cpython/Lib/test/test_asyncio/test_tasks.py", line 1363, in gen
    self.assertAlmostEqual(10.0, when)
AssertionError: 10.0 != 0 within 7 places (10.0 difference)

======================================================================
FAIL: test_run_coroutine_threadsafe_task_factory_exception (test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests)
Test coroutine submission from a tread to an event loop
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/serhiy/py/cpython/Lib/test/test_asyncio/test_tasks.py", line 3189, in test_run_coroutine_threadsafe_task_factory_exception
    self.assertEqual(len(callback.call_args_list), 1)
AssertionError: 2 != 1

----------------------------------------------------------------------
...
(the full log is too long)
msg326826 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-10-01 20:42
> We should raise a DeprecationWarning in Python 3.8 and 3.9 and remove it in 4.0.

On python-committers, it has been said that 3.10 will follow Python 3.9, no?
msg326832 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-10-01 22:08
> On python-committers, it has been said that 3.10 will follow Python 3.9, no?

Victor, you're looking at an outdated comment on this issue.  This is what's in the master branch:

https://github.com/python/cpython/blob/d4c76d960b8b286b75c933780416ace9cda682fd/Lib/asyncio/tasks.py#L598-L599
msg326862 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-10-02 07:40
> Victor, you're looking at an outdated comment on this issue.

No, I read the commit:

https://github.com/python/cpython/commit/558c49bcf3a8543d64a68de836b5d855efd56696


        warnings.warn("The loop argument is deprecated and scheduled for"
                      "removal in Python 4.0.",
                      DeprecationWarning, stacklevel=2)

> This is what's in the master branch:

https://github.com/python/cpython/blob/d4c76d960b8b286b75c933780416ace9cda682fd/Lib/asyncio/tasks.py#L598-L599

Ah, you forgot to mention this bpo in your commit:

commit fad6af2744c0b022568f7f4a8afc93fed056d4db
Author: Yury Selivanov <yury@magic.io>
Date:   Tue Sep 25 17:44:52 2018 -0400

    asyncio/docs: Replace Python 4.0 -> 3.10 (GH-9579)

Anyway, the current code is fine. Thanks.
msg326863 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-10-02 07:41
There is a new PR, so I change the issue resolution again.
msg326864 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-10-02 07:44
FYI if I recall correctly, in the past, we preferred to pass explicitly the loop to avoid to have to get the current loop which may add an overhead. But the current trend is to get rid of the explicit loop parameter.

> asyncio.sleep is a coroutine; passing a *loop* argument to it makes no sense anymore.

sleep() requires the current event loop:

    if loop is None:
        loop = events.get_running_loop()
    else:
        warnings.warn("The loop argument is deprecated and scheduled for "
                      "removal in Python 3.10.",
                      DeprecationWarning, stacklevel=2)

    future = loop.create_future()
    h = loop.call_later(delay,
                        futures._set_result_unless_cancelled,
                        future, result)

Why does it not make sense to pass the loop to sleep? "it makes no sense anymore" something changes?

I'm not against the change, I'm just trying to understand the rationale for other changes :-)
msg326866 - (view) Author: Andrew Svetlov (asvetlov) * (Python committer) Date: 2018-10-02 08:22
My understanding is:

`loop` argument passed to sleep should be always the same as returned from `get_running_loop()`.

Passing it explicitly can be considered as microoptimization but `get_running_loop()` is pretty fast now, no need for such micro-opts.

On another hand passing *non-current* loop is a serious error: nothing prevents to do it but the code just hangs.
msg326890 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-10-02 17:53
New changeset 9012a0fb4c4ec1afef9efb9fdb0964554ea17983 by Yury Selivanov in branch 'master':
bpo-34728: Fix asyncio tests to run under "-Werror" (GH-9661)
https://github.com/python/cpython/commit/9012a0fb4c4ec1afef9efb9fdb0964554ea17983
msg326891 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-10-02 17:59
[victor]
> Why does it not make sense to pass the loop to sleep? "it makes no sense anymore" something changes?

[andrew]
`loop` argument passed to sleep should be always the same as returned from `get_running_loop()`.

What Andrew said.

Basically, it wasn't *ever* possible to pass a loop to sleep() that would be different from the loop that would run it, because sleep() is a *coroutine*.

In asyncio some APIs are functions and some are coroutines.

* asyncio.gather(), for example, is a function.  You can call it from top-level code (and pass an event loop to it) or from a coroutine.

* asyncio.sleep(), wait(), and wait_for() are *coroutines*; they can only be called from other coroutines or if you wrap them into a Task; in all cases, *loop* is always 100% defined for them.

Passing the loop isn't even a viable micro-optimization, it's just pointless.  This extra argument just adds to the confusion and promotes bad patterns, so we want to eventually remove it.
History
Date User Action Args
2018-10-02 17:59:06yselivanovsetstatus: open -> closed
resolution: fixed
messages: + msg326891

stage: patch review -> resolved
2018-10-02 17:53:11yselivanovsetmessages: + msg326890
2018-10-02 08:22:53asvetlovsetmessages: + msg326866
2018-10-02 07:44:32vstinnersetmessages: + msg326864
2018-10-02 07:41:50vstinnersetresolution: fixed -> (no value)
messages: + msg326863
2018-10-02 07:40:47vstinnersetmessages: + msg326862
2018-10-01 22:34:55yselivanovsetstage: needs patch -> patch review
pull_requests: + pull_request9054
2018-10-01 22:08:57yselivanovsetmessages: + msg326832
2018-10-01 20:45:54serhiy.storchakasetstatus: closed -> open
stage: resolved -> needs patch
2018-10-01 20:42:37vstinnersetnosy: + vstinner
messages: + msg326826
2018-10-01 20:40:59serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg326825
2018-09-24 09:55:53willingcsetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2018-09-24 09:51:26willingcsetnosy: + willingc
messages: + msg326213
2018-09-19 12:41:02xtreaksetnosy: + xtreak
2018-09-19 12:35:36fbidusetnosy: + fbidu
2018-09-19 10:29:47python-devsetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request8835
2018-09-18 21:49:28yselivanovsetmessages: + msg325685
2018-09-18 21:44:05yselivanovcreate