Unable to Perform Graceful Shutdown of Asyncio App after a KeyboardInterrupt - python

I am having trouble letting my asyncio app shutdown gracefully when the user creates a KeyboardInterrupt by pressing Ctrl+C.
Why is it still not working properly despite using try... except KeyboardInterrupt? It still shows a traceback and something about a Unclosed AIOKafkaProducer.
import asyncio
from aiokafka import AIOKafkaProducer
class Foo:
def __init__(self):
self.producer = None
async def start(self):
self.producer = AIOKafkaProducer(bootstrap_servers="localhost:29092")
await self.producer.start()
self.loop_task = asyncio.create_task(self.loop())
await asyncio.gather(*[self.loop_task])
async def stop(self):
self.loop_task.cancel()
await self.producer.stop()
async def loop(self):
while True:
await asyncio.sleep(5)
async def main():
foo = Foo()
try:
await foo.start()
except KeyboardInterrupt:
print(
"Attempting graceful shutdown, press Ctrl+C+ again to exit...", flush=True
)
await foo.stop()
if __name__ == "__main__":
asyncio.run(main())
Error messages after pressing pressing Ctrl+C:
^CTraceback (most recent call last):
File "/home/x/test.py", line 37, in <module>
asyncio.run(main())
File "/opt/anaconda3/envs/testenv/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/opt/anaconda3/envs/testenv/lib/python3.9/asyncio/base_events.py", line 629, in run_until_complete
self.run_forever()
File "/opt/anaconda3/envs/testenv/lib/python3.9/asyncio/base_events.py", line 596, in run_forever
self._run_once()
File "/opt/anaconda3/envs/testenv/lib/python3.9/asyncio/base_events.py", line 1854, in _run_once
event_list = self._selector.select(timeout)
File "/opt/anaconda3/envs/testenv/lib/python3.9/selectors.py", line 469, in select
fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
Unclosed AIOKafkaProducer
producer: <aiokafka.producer.producer.AIOKafkaProducer object at 0x7f643c0d0c40>
Adding try...except around main()
if __name__ == "__main__":
try:
asyncio.run(main())
except Exception as e:
print("Exception:", e)
This gave the same traceback

Related

Python websocket in a thread

I try to run a websocket in python in a thread. Here is the example I made:
import asyncio
import websockets
import threading
class websocket(threading.Thread):
def run(self):
asyncio.get_event_loop().run_until_complete(
websockets.serve(self.echo, 'localhost', 8765))
asyncio.get_event_loop().run_forever()
async def echo(websocket, path):
async for message in websocket:
await websocket.send(message)
ws = websocket()
ws.start()
I get that error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/home/xavier/dev/3003_mdt/mdt-robotarm/robotarm_server.py", line 7, in run
asyncio.get_event_loop().run_until_complete(
File "/usr/lib/python3.6/asyncio/events.py", line 694, in get_event_loop
return get_event_loop_policy().get_event_loop()
File "/usr/lib/python3.6/asyncio/events.py", line 602, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.
Process finished with exit code 0
How can I run the websocket in a thread?
You've got the error because get_event_loop creates the loop automatically only in the main thread. Create the loop manually with new_event_loop like this:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(
websockets.serve(self.echo, "localhost", 8765)
)
loop.close()

RuntimeError when multithreading python "RuntimeError: There is no current event loop in thread 'Thread-1'."

I am trying to run multiple discord bots with the same script at once. The way I am doing this is going down a csv file, to get the bot token and bot name. I then start my script:
import time, csv, re, random, string, sys, os, asyncio
from datetime import datetime
from threading import Thread
import discord
from dotenv import load_dotenv
from discord_webhook import DiscordWebhook, DiscordEmbed
def main(bottoken, botname):
TOKEN = (bottoken)
client = discord.Client()
#client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
#client.event
async def on_message(message):
#do stuff
print('Do stuff')
client.run(TOKEN)
def runtask():
if __name__ == '__main__':
with open('botinfo.csv', 'r') as f:
reader = csv.reader(f, delimiter=',')
for i, row in enumerate(reader):
Thread(target = main, args=(row[0], row[1])).start()
if __name__ == '__main__':
runtask()
But then I get this error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/Users/alexforman/Documents/GitHub/bot-price-monitor/frontend_multithread.py", line 14, in main
client = discord.Client()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/discord/client.py", line 216, in __init__
self.loop = asyncio.get_event_loop() if loop is None else loop
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.
Exception in thread Thread-2:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/Users/alexforman/Documents/GitHub/bot-price-monitor/frontend_multithread.py", line 14, in main
client = discord.Client()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/discord/client.py", line 216, in __init__
self.loop = asyncio.get_event_loop() if loop is None else loop
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-2'.
Does anyone have any ideas on how I could fix it?
You don't need to start a separate thread for each bot, the asyncio event loop is perfectly capable of serving all bots at once. (That's the kind of thing event loops are designed for.)
You just need to start all the bots as coroutines of the same asyncio event loop:
async def run_client(bottoken, botname):
TOKEN = (bottoken)
client = discord.Client()
#client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
#client.event
async def on_message(message):
#do stuff
print('Do stuff')
# can't use client.run() because it would block the event loop
# but we can use client.start(), which is a coroutine that does
# the same thing (and which is internally called by client.run)
await client.start(TOKEN)
async def main():
coros = []
with open('botinfo.csv', 'r') as f:
reader = csv.reader(f, delimiter=',')
for i, row in enumerate(reader):
coros.append(run_client(row[0], row[1]))
await asyncio.gather(*coros)
if __name__ == '__main__':
asyncio.run(main())

Why does calling asyncio.sleep causes websocket connection to close?

I am trying to build a websockets server where the client connects and sends a request of some kind, and then the server adds that request to a queue and returns something back when finished.
to simulate the process of queueing and waiting for tasks to finish, I called asyncio.sleep whenever the process is supposed to be called.
however, whenever I do that I get the following error from the client-side:
Traceback (most recent call last):
File "D:/Python/server/client1.py", line 10, in <module>
asyncio.get_event_loop().run_until_complete(message())
File "C:\ProgramData\Anaconda3\envs\server\lib\asyncio\base_events.py", line 587, in run_until_complete
return future.result()
File "D:/Python/server/client1.py", line 8, in message
print(await socket.recv())
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 509, in recv
await self.ensure_open()
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 812, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: code = 1000 (OK), no reason
and this error from the server-side:
Task exception was never retrieved
future: <Task finished coro=<queue_dispatcher() done, defined at D:/RamNew2020/Python/server/asyncserver.py:39> exception=ConnectionClosedOK('code = 1000 (OK), no reason')>
Traceback (most recent call last):
File "D:/Python/server/asyncserver.py", line 45, in queue_dispatcher
await data_from_q.sendto_websocket("yay")
File "D:/Python/server/asyncserver.py", line 20, in sendto_websocket
await self.mywebsocket.send(message)
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 555, in send
await self.ensure_open()
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 803, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: code = 1000 (OK), no reason
My scripts:
server.py:
import asyncio
import websockets
import datetime
import random
queue = asyncio.Queue()
async def rnd_sleep(t):
# sleep for T seconds on average
await asyncio.sleep(t * random.random() * 2)
#When a client sends a message the information is saved in a Request object,
#including the socket connection
class Request:
def __init__(self, mywebsocket, mydat):
self.mywebsocket = mywebsocket
self.mydat = mydat
self.start_time_of_request = datetime.datetime.now()
self.id = random.randint(0, 128)
async def sendto_websocket(self, message):
await self.mywebsocket.send(message)
def get_dat(self):
return self.mydat
def get_start_time(self):
return self.start_time_of_request
async def add_to_queue(websocket, path):
global queue
dat = await websocket.recv()
base64dat = Request(websocket, dat)
await queue.put(base64dat)
#problem in queue_dispatcher
async def queue_dispatcher(q):
while True:
data_from_q = await q.get()
print(data_from_q.id)
await asyncio.sleep(1) #The problem is here, whenever I remove it, it works
await data_from_q.sendto_websocket("yay")
q.task_done()
async def main():
global queue
asyncio.create_task(queue_dispatcher(queue))
pass
start_server = websockets.serve(add_to_queue, 'localhost', 5000)
asyncio.get_event_loop().run_until_complete(main())
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
client.py:
import asyncio
async def message():
async with websockets.connect("ws://localhost:5000")as socket:
msg = input("What do you want to send: ")
await socket.send(msg)
print(await socket.recv())
asyncio.get_event_loop().run_until_complete(message())
In addition, whenever I try to await any other async function, it works fine no matter how long.

How to send a message from the server to the client in a web socket

Well I'm doing a little program that aims to receive messages on rabbitmq and send to clients connected to the websocket that has the same index, but having a problem when sending to the client, I reused a code that had to work in 3.6 .9 and I don't remember the version of the tornado (websocket library that I use) but I changed the pc and I'm able to install it again, now I have the newest versions of the library and python.
I'll post my old code because it's easier to understand because of the msm error
import tornado.websocket
import tornado.ioloop
import threading
import pika
import json
def verificar_novo(se):
for i in range(0,len(conexao_lista)):
if se == conexao_lista[i]["endereco"]:
return 0
return 1
def excluir_conexao(endereco):
for i in range(0,len(conexao_lista)):
if conexao_lista[i]["endereco"] == endereco:
del(conexao_lista[i])
break
""" Função para procurar mensagens no rabbit e retornar para os clientes"""
def callback(ch, method, properties, body):
menssagem_rabbit = json.loads(body)
threading.Lock()
for i in range(0, len(conexao_lista)):
if (conexao_lista[i]["configuracao"]["ras_eve_id_indice"]) == (menssagem_rabbit["ras_eve_id_indice"]):
conexao_lista[i]["endereco"].write_message(menssagem_rabbit)
break
threading.RLock()
""" Classe de conexao com cliente"""
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print("Novo cliente conectado")
def on_close(self):
print("Cliente desconectado")
excluir_conexao(self)
def on_message(self, message):
n = verificar_novo(self)
if n == 0:
self.write_message(u"Sua menssagem: " + message)
else:
dados_json = json.loads(message)
conexao_dicionario["endereco"] = self
conexao_dicionario["configuracao"] = dados_json
conexao_lista.append(conexao_dicionario.copy())
self.write_message(u"Usuario conectado " + dados_json["id_usuario"])
def check_origin(self, origin):
return True
"""Função que a thread ficara rodando para consumir as mensagem em segundo plano"""
def procurar_mensagens():
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.basic_consume(queue='testerenan', on_message_callback=callback, auto_ack=True)
channel.start_consuming()
"""Variaveis"""
conexao_lista = []
conexao_dicionario = {"endereco": "","configuracao":""}
"""Chamando a Thread"""
threading.Thread(target=procurar_mensagens, args=()).start()
"""Conexão do WebSocket"""
application = tornado.web.Application([(r"/", WebSocketHandler),])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
The error that appears:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/usr/lib/python3.8/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/home/renan/Área de Trabalho/Projeto-WebSocket/Servidor.py", line 63, in procurar_mensagens
channel.start_consuming()
File "/usr/local/lib/python3.8/dist-packages/pika/adapters/blocking_connection.py", line 1866, in start_consuming
self._process_data_events(time_limit=None)
File "/usr/local/lib/python3.8/dist-packages/pika/adapters/blocking_connection.py", line 2027, in _process_data_events
self.connection.process_data_events(time_limit=time_limit)
File "/usr/local/lib/python3.8/dist-packages/pika/adapters/blocking_connection.py", line 834, in process_data_events
self._dispatch_channel_events()
File "/usr/local/lib/python3.8/dist-packages/pika/adapters/blocking_connection.py", line 566, in _dispatch_channel_events
impl_channel._get_cookie()._dispatch_events()
File "/usr/local/lib/python3.8/dist-packages/pika/adapters/blocking_connection.py", line 1493, in _dispatch_events
consumer_info.on_message_callback(self, evt.method,
File "/home/renan/Área de Trabalho/Projeto-WebSocket/Servidor.py", line 26, in callback
conexao_lista[i]["endereco"].write_message(menssagem_rabbit)
File "/home/renan/.local/lib/python3.8/site-packages/tornado/websocket.py", line 342, in write_message
return self.ws_connection.write_message(message, binary=binary)
File "/home/renan/.local/lib/python3.8/site-packages/tornado/websocket.py", line 1098, in write_message
fut = self._write_frame(True, opcode, message, flags=flags)
File "/home/renan/.local/lib/python3.8/site-packages/tornado/websocket.py", line 1075, in _write_frame
return self.stream.write(frame)
File "/home/renan/.local/lib/python3.8/site-packages/tornado/iostream.py", line 555, in write
future = Future() # type: Future[None]
File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-1'.
I'll leave the project for download if anyone wants to take a look:
https://github.com/Renan-Sacca/Projeto-WebSocket
In general, you have to be very careful when mixing threads and Tornado - you can't call most tornado methods from other threads (this has always been true, but the library got more strict about enforcing it in Tornado 5.0). In particular this includes write_message. So in callback, instead of calling write_message, you have to ask the IOLoop to call it for you.
In the main block, do global main_io_loop; main_io_loop = IOLoop.current() to save the main threads' IOLoop so you can refer to it later. Then in callback replace the call to write_message with
main_io_loop.add_callback(conexao_lista[i]["endereco"].write_message, menssagem_rabbit)

Avoid traceback after CTRL+C in async

I'm currently exploring async/await in python.
I'd like to start Pyrogram, then run the cronjob function. I created this code:
import asyncio
from pyrogram import Client, Filters, MessageHandler
import time
app = Client("pihome")
async def cronjob():
print("Cronjob started")
while True:
#print(f"time is {time.strftime('%X')}")
print(int(time.time()))
if int(time.strftime('%S')) == 10:
print("SECONDO 10") # Change this with checks in future
await asyncio.sleep(1)
async def main():
print(f"Starting {app.session_name}...\n")
# Pyrogram
await app.start()
print("Pyrogram started")
# Cronjob
loop = asyncio.new_event_loop()
loop.run_until_complete(loop.create_task(await cronjob()))
print("\nDone")
await Client.idle()
await app.stop()
print(f"\nStopping {app.session_name}...")
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
But when i want to stop it with Ctrl+C it gives me this traceback:
^CTraceback (most recent call last):
File "pihome.py", line 39, in <module>
asyncio.get_event_loop().run_until_complete(main())
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 603, in run_until_complete
self.run_forever()
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 570, in run_forever
self._run_once()
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 1823, in _run_once
event_list = self._selector.select(timeout)
File "/usr/local/lib/python3.8/selectors.py", line 468, in select
fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
How can I solve it? Also, try/except blocks seem not working
To run the application, it is better to use asyncio.run(main()). In my opinion, it’s more clear.
Replace loop = asyncio.new_event_loop() by loop = asyncio.get_event_loop(). Because using asyncio.new_event_loop() after asyncio.get_event_loop().run_until_complete(main()) you create a second event loop in main thread which is prohibited. Only one event loop per thread is permitted!
Remove await from this line:
loop.run_until_complete(loop.create_task(await cronjob()))
Because create_task need to Coroutine as the first argument but you pass it None

Categories

Resources