Reputation: 27985
I'm trying to wrap my head around asyncio
in Python. I wrote this little programm that when invoked will first print
Server booting
Do stuff called
and then after one second
Async Thingy
That is exactly what it is supposed to do but it's not quite the way I want it yet.
Basically this mimics a Server
that wants to create a PeerPool
in __init__
which depends on ThingThatWeAreWaitingOn
. I want to be able to create the PeerPool
in __init__
and pass an Awaitable[ThingThatWeAreWaitingOn]
that the PeerPool
can use as soon as it is ready. Again, this seems to work just fine but the catch is, as the code stands now, we kick off the task to resolve ThingThatWeAreWaitingOn
directly from within __init__
but ideally I'd like to be able to kick that off from within run()
.
How would I do that?
import asyncio
from typing import (
Awaitable,
Any
)
class ThingThatWeAreWaitingOn():
name = "Async Thingy"
class PeerPool():
def __init__(self, discovery: Awaitable[ThingThatWeAreWaitingOn]):
self.awaitable_discovery = discovery
def do_stuff(self):
print("Do stuff called")
self.awaitable_discovery.add_done_callback(lambda d: print(d.result().name))
class Server():
def __init__(self):
# This immediately kicks of the async task but all I want is to
# create a Future to pass that would ideally be kicked off in
# the run() method
self.fut_discovery = asyncio.ensure_future(self.get_discovery())
self.peer_pool = PeerPool(self.fut_discovery)
async def get_discovery(self):
await asyncio.sleep(1)
return ThingThatWeAreWaitingOn()
def run(self):
loop = asyncio.get_event_loop()
print("Server booting")
# Here is where I want to "kick off" the self.fut_discovery but how?
# self.fut_discovery.kick_off_now()
self.peer_pool.do_stuff()
loop.run_forever()
server = Server()
server.run()
Here's a link to a runnable demo: https://repl.it/repls/PleasedHeavenlyLock
Upvotes: 3
Views: 1712
Reputation: 39516
If I understand everything right, you want something like this:
class Server():
def __init__(self):
self.fut_discovery = asyncio.Future()
self.peer_pool = PeerPool(self.fut_discovery)
async def get_discovery(self):
await asyncio.sleep(1)
return ThingThatWeAreWaitingOn()
def run(self):
loop = asyncio.get_event_loop()
print("Server booting")
async def discovery_done():
res = await self.get_discovery()
self.fut_discovery.set_result(res)
asyncio.ensure_future(discovery_done()) # kick discovery to be done
self.peer_pool.do_stuff()
loop.run_forever()
You may want to rewrite code somehow to make in clearer. Right now it's not very clear what you're going to do and which part of code depends of which.
For example, awaitable_discovery
name is misleading: plain awaitable not necessary has add_done_callback
method. If you're planing to use this method, signature
class PeerPool():
def __init__(self, fut_discovery: asyncio.Future):
will make more sense.
May be you should create class for discovery. You can inherit asyncio.Future
or implement __await__
magic method to make it's objects future-like/awaitable.
Upvotes: 2