Running a coroutine at set intervals

208 views
Skip to first unread message

Wellington Cordeiro

unread,
Aug 4, 2015, 11:46:19 PM8/4/15
to python-tulip
I'm trying to add a system where a "heartbeat" message is sent to a server every n seconds by my client, but I'm having trouble with the loop.call_later or call_at methods. They use callbacks but I'm trying to run a coroutine at the set interval without blocking. The conditions I'm trying to work within are

1. countdown from n, when we reach zero send the heartbeat
2. If a message is received from the server before we hit zero, reset the timer
3. If we send a different message before we hit zero, reset the timer

I'm just not sure how to implement something like this with asyncio, I tried using the threading.Timer class but since that creates another thread my client called the loop.stop() method before the interval and closed its connection early.

Guido van Rossum

unread,
Aug 5, 2015, 6:02:24 AM8/5/15
to Wellington Cordeiro, python-tulip
To start a coroutine from a callback, you wrap the coroutine in a Task. That's all. E.g. (untested)

from asyncio import coroutine, get_event_loop

@coroutine
def coro():
    ...
    yield from something()
    ...

# callback
def heartbeat():
    get_event_loop().create_task(coro())
    get_event_loop().call_later(n, heartbeat)

heartbeat()  # Get it all started
--
--Guido van Rossum (python.org/~guido)

Wellington Cordeiro

unread,
Aug 5, 2015, 1:21:22 PM8/5/15
to python-tulip, willy...@gmail.com, gu...@python.org
How do you unwrap the result of the coroutine wrapped in the task though?

Guido van Rossum

unread,
Aug 5, 2015, 1:27:01 PM8/5/15
to Wellington Cordeiro, python-tulip
You can't have it both ways. If it's a coroutine, you can't get its result in the callback. But that shouldn't be a problem -- you can just put any processing of the result you need in the coroutine. In effect, the only purpose of the callback then is to start the coroutine running. If you have an existing coroutine whose result you want to process, just wrap it in another coroutine which calls it (using yield from) and then does the processing.

Wellington Cordeiro

unread,
Aug 6, 2015, 1:01:12 PM8/6/15
to python-tulip, willy...@gmail.com, gu...@python.org
Maybe I'm using asyncio improperly but currently I have a class that I'm writing that acts as the client. I'm trying to expose the API to it so that I'm able to use it like

client = ClientClass()

try:
    client
.start()

    client
.some_message_method()

    client
.other_message_method()
except KeyboardInterrupt:
    client
.stop()

Now the client, would be interacted with by other code that is synchronous so that may be my problem and the calls to other methods may not come immediately after start(),
so the start method would create the connection, send a logon message to the server and then send the heartbeats at the set intervals (resetting the interval when a different method is called.)

With coroutines I was able to run them with loop.run_until_complete and then return the response messages to the synchronous code. With tasks though there doesn't seem to be a way.

Guido van Rossum

unread,
Aug 7, 2015, 5:51:24 AM8/7/15
to Wellington Cordeiro, python-tulip
Maybe someone else on this list can help you off-list? Teasing out the solution seems to be requiring a lot of back-and-forth.
Reply all
Reply to author
Forward
0 new messages