[Python-Dev] Using async/await in place of yield expression (original) (raw)
Terry Reedy tjreedy at udel.edu
Tue Nov 28 00:56:48 EST 2017
- Previous message (by thread): [Python-Dev] Using async/await in place of yield expression
- Next message (by thread): [Python-Dev] Using async/await in place of yield expression
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On 11/27/2017 5:05 PM, Guido van Rossum wrote:
On Mon, Nov 27, 2017 at 1:58 PM, Greg Ewing <greg.ewing at canterbury.ac.nz_ _<mailto:greg.ewing at canterbury.ac.nz>> wrote:
Guido van Rossum wrote: The source for sleep() isn't very helpful -- e.g. @coroutine is mostly a backwards compatibility thing. So how are you supposed to write that without using @coroutine? A simplified version using async def/await:
async def sleep(delay): f = Future()
This must be asyncio.Future as (by experiment) a concurrent.futures.Future cannot be awaited. The a.F doc could add this as a difference. Future needs an argument for the keyword-only loop parameter; as I remember, the default None gets replaced by the default asyncio loop.
geteventloop().calllater(delay, f.setresult)
A result is needed, such as None or delay, to pass to f.set_result.
await f
I gather that
- the value of the expression is the result set on the future, which would normally be needed, though not here;
- the purpose of 'await f' here is simply to block exit from the coroutine, without blocking the loop, so that users of 'await sleep(n)' will actually pause (without blocking other code).
Since a coroutine must be awaited, and not just called, and await can only be used in a coroutine, there seems to be a problem of where to start. The asyncio answer, in the PEP, is to wrap a coroutine call in a Task, which is, as I remember, done by the loop run methods.
Based on the notes above, and adding some prints, I got this to run:
import asyncio import time
loop = asyncio.get_event_loop()
async def sleep(delay): f = asyncio.Future(loop=loop) loop.call_later(delay, f.set_result, delay) print('start') start = time.perf_counter() d = await f stop = time.perf_counter() print(f'requested sleep = {d}; actual = {stop-start:f}')
loop.run_until_complete(sleep(1))
This produces: start requested sleep = 1; actual = .9--- [usually < 1]
Now, the question I've had since async and await were introduced, is how to drive async statements with tkinter. With the help of the working example above, I make a start.
from asyncio import Future, Task import tkinter as tk import time
class ATk(tk.Tk): "Enable tkinter program to use async def, etc, and await sleep."
def __init__(self):
super().__init__()
def task(self, coro):
"Connect async def coroutine to tk loop."
Task(coro, loop=self)
def get_debug(self):
"Internal method required by Future."
print('debug')
return False
def call_soon(self, callback, *args):
"Internal method required by Task and Future."
# TaskStep/Wakeup/MethWrapper has no .__name__ attribute.
# Tk.after requires callbacks to have one (bug, I think).
print('soon', callback, *args, hasattr(callback, '__name__'))
def wrap2(): callback(*args)
return self.after(0, wrap2)
root = ATk()
async def sleep(delay): f = Future(loop=root) def cb(): print('cb called') f.set_result(delay) root.after(int(delay*1000), cb) print('start') start = time.perf_counter() d = await f stop = time.perf_counter() print(f'requested sleep = {d}; actual = {stop-start:f}')
root.task(sleep(1)) root.mainloop()
Output: debug soon <TaskStepMethWrapper object at 0x000001AE982E1748> False debug start cb called soon <TaskWakeupMethWrapper object at 0x000001AE988E04C8> False requested sleep = 1; actual = 1.01--- [always about 1.01]
Replacing the last two lines with
async def myloop(seconds): while True: print(f'*** {seconds} ***') await sleep(seconds)
root.task(myloop(1.2)) root.task(myloop(.77)) root.mainloop()
prints interleaved 1.2 and .77 lines.
I will next work on animating tk widgets in the loops.
-- Terry Jan Reedy
- Previous message (by thread): [Python-Dev] Using async/await in place of yield expression
- Next message (by thread): [Python-Dev] Using async/await in place of yield expression
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]