I am trying to call async functions in tornado overriden methods.
Initially, the function generate() is syncronous, but that seems to block the entire program, including on_connection_close method
class Reader:
def __init__(self, topic, group_id):
self.topic = topic
self.consumer = AIOKafkaConsumer(topic, bootstrap_servers="kafka:9092",
group_id=group_id)
async def read(self, sender):
print("enter read...")
#it seems the code below is blocking
await self.consumer.start()
async for message in self.consumer:
sender(json.dumps(consumed_message))
await self.consumer.commit()
class SampleSockeHandler(tornado.websocket.WebSocketHandler):
def on_connection_close(self):
self.app.close()
def on_message(self, message):
generate(message)
#await generate(message)
async def generate(self, message):
await self.reader.read(message)
print("enter read...") is only executed once in the first read() call. In the subsequent read() calls, it does not print anymore.
Now when I comment out all the code below it i.e
await self.consumer.start()
async for message in self.consumer:
sender(json.dumps(consumed_message))
await self.consumer.commit()
it works again
How do I fix this problem? Shouldn't it be an async call?
async def main():
app = Application()
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
asyncio.run(main())
Related
I am trying to implement an interface using the help of some code that is async. How can I do this?
#!/usr/bin/env python3
import asyncio
class SomeInterface():
def some_method(self, s):
pass
class MyImpl(SomeInterface):
def __init__(self, helper_async_obj):
self.helper = helper_async_obj
def some_method(self, s):
print("before using async helper")
# TODO: I want to call self.helper.f(s) here. If this was an async method, I would just do:
# await self.helper.f(s)
print("after async helper is done")
class AsyncHelper():
async def f(self, s):
print(f"before sleep: {s}")
await asyncio.sleep(1)
print(f"after sleep: {s}")
async def main():
ah = AsyncHelper()
my_impl = MyImpl(ah)
my_impl.some_method("hello")
# wait forever
await asyncio.get_running_loop().create_future()
if __name__ == "__main__":
asyncio.run(main())
I expect the output to look like this:
before using async helper
before sleep: hello
after sleep: hello
after using async helper
The issue is that I want to run an async method from a sync method and I want the async method to finish executing before I return from the sync method. How can I do this? I can't just do task = asyncio.create_task(self.helper.f(s)) because that will return immediately and execute in the background, not block the caller until the task is done.
I tested the following code with Ncat. It only sends a single message, then it does not send anything, and does not except. It is also not reading anything.
I have no idea what could be going on. There is no exception, and no data seems to be sent.
import asyncio
loop = asyncio.get_event_loop()
class Client:
def __init__(self):
self.writer = None
self.reader = None
async def connect(self, address, port):
reader, writer = await asyncio.open_connection(address, port)
self.reader = reader
self.writer = writer
print("connected!")
async def send(self, message):
print("writing " + message)
self.writer.write((message + '\n').encode())
await self.writer.drain()
async def receive(self):
print("receiving")
message = (self.reader.readuntil('\n'.encode())).decode()
return message
async def read_loop(self):
while True:
incoming = await self.receive()
print("remote: " + incoming)
async def main():
client = Client()
await client.connect("127.0.0.1", 31416)
loop.create_task(client.read_loop())
while True:
text = input("message: ")
await client.send(text)
loop.run_until_complete(main())
I haven't tested your code but there are things that immediately jump out.
First, you should avoid to use low-level asyncio APIs calling loop.create_task or loop.run_until_complete. Those are intended to be used by frameworks developers. Use asyncio.create_task and asyncio.run instead.
Then, you are getting no exception (or result) because you are not keeping any reference to the task you are creating. You should always keep one to tasks (i.e. assigning it to a variabile or adding it in a list/set).
Last, you are missing an await in the receive method: StreamReader.readuntil is a coroutine function and so you have to await on it.
class Client:
...
async def receive(self):
print('receiving...')
message = await self.reader.readuntil('\n'.encode())
return message.decode()
...
I need to be able to keep adding coroutines to the asyncio loop at runtime. I tried using create_task() thinking that this would do what I want, but it still needs to be awaited.
This is the code I had, not sure if there is a simple edit to make it work?
async def get_value_from_api():
global ASYNC_CLIENT
return ASYNC_CLIENT.get(api_address)
async def print_subs():
count = await get_value_from_api()
print(count)
async def save_subs_loop():
while True:
asyncio.create_task(print_subs())
time.sleep(0.1)
async def start():
global ASYNC_CLIENT
async with httpx.AsyncClient() as ASYNC_CLIENT:
await save_subs_loop()
asyncio.run(start())
I once created similar pattern when I was mixing trio and kivy, which was demonstration of running multiple coroutines asynchronously.
It use a trio.MemoryChannel which is roughly equivalent to asyncio.Queue, I'll just refer it as queue here.
Main idea is:
Wrap each task with class, which has run function.
Make class object's own async method to put object itself into queue when execution is done.
Create a global task-spawning loop to wait for the object in queue and schedule execution/create task for the object.
import asyncio
import traceback
import httpx
async def task_1(client: httpx.AsyncClient):
resp = await client.get("http://127.0.0.1:5000/")
print(resp.read())
await asyncio.sleep(0.1) # without this would be IP ban
async def task_2(client: httpx.AsyncClient):
resp = await client.get("http://127.0.0.1:5000/meow/")
print(resp.read())
await asyncio.sleep(0.5)
class CoroutineWrapper:
def __init__(self, queue: asyncio.Queue, coro_func, *param):
self.func = coro_func
self.param = param
self.queue = queue
async def run(self):
try:
await self.func(*self.param)
except Exception:
traceback.print_exc()
return
# put itself back into queue
await self.queue.put(self)
class KeepRunning:
def __init__(self):
# queue for gathering CoroutineWrapper
self.queue = asyncio.Queue()
def add_task(self, coro, *param):
wrapped = CoroutineWrapper(self.queue, coro, *param)
# add tasks to be executed in queue
self.queue.put_nowait(wrapped)
async def task_processor(self):
task: CoroutineWrapper
while task := await self.queue.get():
# wait for new CoroutineWrapper Object then schedule it's async method execution
asyncio.create_task(task.run())
async def main():
keep_running = KeepRunning()
async with httpx.AsyncClient() as client:
keep_running.add_task(task_1, client)
keep_running.add_task(task_2, client)
await keep_running.task_processor()
asyncio.run(main())
Server
import time
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return str(time.time())
#app.route("/meow/")
def meow():
return "meow"
app.run()
Output:
b'meow'
b'1639920445.965701'
b'1639920446.0767004'
b'1639920446.1887035'
b'1639920446.2986999'
b'1639920446.4067013'
b'meow'
b'1639920446.516704'
b'1639920446.6267014'
...
You can see tasks running repeatedly on their own pace.
Old answer
Seems like you only want to cycle fixed amount of tasks.
In that case just iterate list of coroutine with itertools.cycle
But this is no different with synchronous, so lemme know if you need is asynchronous.
import asyncio
import itertools
import httpx
async def main_task(client: httpx.AsyncClient):
resp = await client.get("http://127.0.0.1:5000/")
print(resp.read())
await asyncio.sleep(0.1) # without this would be IP ban
async def main():
async with httpx.AsyncClient() as client:
for coroutine in itertools.cycle([main_task]):
await coroutine(client)
asyncio.run(main())
Server:
import time
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return str(time.time())
app.run()
Output:
b'1639918937.7694323'
b'1639918937.8804302'
b'1639918937.9914327'
b'1639918938.1014295'
b'1639918938.2124324'
b'1639918938.3204308'
...
asyncio.create_task() works as you describe it. The problem you are having here is that you create an infinite loop here:
async def save_subs_loop():
while True:
asyncio.create_task(print_subs())
time.sleep(0.1) # do not use time.sleep() in async code EVER
save_subs_loop() keeps creating tasks but control is never yielded back to the event loop, because there is no await in there. Try
async def save_subs_loop():
while True:
asyncio.create_task(print_subs())
await asyncio.sleep(0.1) # yield control back to loop to give tasks a chance to actually run
This problem is so common I'm thinking python should raise a RuntimeError if it detects time.sleep() within a coroutine :-)
You might want to try the TaskThread framework
It allows you to add tasks in runtime
Tasks are re-scheduled periodically (like in your while loop up there)
There is a consumer / producer framework built in (parent/child relationships) which you seem to need
disclaimer: I wrote TaskThread out of necessity & it's been a life saver.
I hope websockets can running in background.
Once received the message, print it.
Other function can call it's 'send' function to send message
But the code under 'loop.run_until_complete(Iao())' will no running.
I tried use 'threading' then put websockets in other thread, it's still can't running.
Is it possible to achieve it? except create a async function
import asyncio
import websockets
import time
class WebsocketHandler():
def __init__(self):
self.conn = None
async def connect(self, url):
self.conn = await websockets.connect(url)
async def send(self, msg):
await self.conn.send(msg)
async def receive(self):
while True:
print(await self.conn.recv())
async def close(self):
await self.conn.close()
async def Iao():
global handler
handler = WebsocketHandler()
await handler.connect('ws://localhost:8765')
await handler.receive()
await handler.send('{"action":"plus"}')
await handler.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(Iao())
while True:
print("1")
asyncio.run(handler.send('{"action":"plus"}'))
print("2")
time.sleep(2)
I have this basic exchange monitor script. I'm trying to create one thread per symbol, apart from the main thread which is handling other work, and have them listen to public Gemini websocket endpoints. I'm getting the first thread running and printing exchange data to the console, but not the second one. I had expected to see data from both threads being printed at approximately the same time. I've tried using the threading library instead of asyncio and encountered the same situation.
I realize my two public API MarketWebsocket classes could be combined to be cleaner, I'm still trying to work out a way to easily add other symbols to the list. Thanks for any nudges in the right direction!
import asyncio
from websockets import connect
symbols_to_watch = [
"BTCUSD",
"ETHUSD"
]
class BTCMarketWebsocket:
disable = False
async def __aenter__(self):
symbol = symbols_to_watch[0]
self._conn = connect("wss://api.gemini.com/v1/marketdata/{}".format(symbol))
self.websocket = await self._conn.__aenter__()
return self
async def __aexit__(self, *args, **kwargs):
await self._conn.__aexit__(*args, **kwargs)
async def receive(self):
return await self.websocket.recv()
class ETHMarketWebsocket:
disable = False
async def __aenter__(self):
symbol = symbols_to_watch[1]
self._conn = connect("wss://api.gemini.com/v1/marketdata/{}".format(symbol))
self.websocket = await self._conn.__aenter__()
return self
async def __aexit__(self, *args, **kwargs):
await self._conn.__aexit__(*args, **kwargs)
async def receive(self):
return await self.websocket.recv()
async def btcMarketWebsocket():
async with BTCMarketWebsocket() as btcMarketWebsocket:
while not btcMarketWebsocket.disable:
print(await btcMarketWebsocket.receive())
async def ethMarketWebsocket():
async with ETHMarketWebsocket() as ethMarketWebsocket:
while not ethMarketWebsocket.disable:
print(await ethMarketWebsocket.receive())
if __name__ == '__main__':
asyncio.run(btcMarketWebsocket())
asyncio.run(ethMarketWebsocket())
You can do
async def multiple_tasks():
Tasks =[]
Tasks.append(btcMarketWebsocket())
Tasks.append(ethMarketWebsocket())
await asyncio.gather(*Tasks, return_exceptions=True)
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(multiple_tasks())