I can't launch the browser in a separate thread.
I have this code:
import asyncio
from pyppeteer import launch
class browser(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(self.main())
async def main(self):
url = 'https://example.com'
browser = await launch(headless=True)
page = await browser.newPage()
await page.goto(url, waitUntil='networkidle0')
When calling, this error comes out:
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread of the main interpreter
If you run it without QThread then everything works
Related
I have a "listener" loop that constantly watches for items to process from an asyncio queue. This loop runs in a part of the application that is not using asyncio, so I've been trying to set up a passive asyncio main loop that the listener can be transferred to as needed. The listener is started and stopped as needed per input from the user.
For some reason the code below never results in the listener() actually running (i.e. print("Listener Running") is never printed). start_IOLoop_thread is run at startup of the application.
Can anyone point out what the problem is with this setup? Please let me know if more info is needed.
Edit: replaced code with a runnable example per the comments:
import asyncio
import threading
from asyncio.queues import Queue
import time
class Client:
def __init__(self):
self.streamQ = Queue()
self.loop = None
self.start_IOLoop_thread()
self.stream_listener()
def stream_listener(self):
self.streaming = True
async def listener():
print("Listener Running")
while self.streaming:
data = await self.streamQ.get()
# DEBUG
print(data)
print("Listener Stopped")
print("Starting Listener")
self.listener = asyncio.run_coroutine_threadsafe(listener(), self.loop)
def start_IOLoop_thread(self):
async def inf_loop():
# Keep the main thread alive and doing nothing
# so we can freely give it tasks as needed
while True:
await asyncio.sleep(1)
async def main():
await inf_loop()
def start_IO():
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
asyncio.run(main())
print("Main Exited")
threading.Thread(target=start_IO, daemon=True).start()
# A small delay is needed to give the loop time to initialize,
# otherwise self.loop is passed as "None"
time.sleep(0.1)
if __name__ == "__main__":
C = Client()
input("Enter to exit")
You never start the newly created loop. I adjusted to call main (although here it does nothing I assume the original code is more complex). All changes are in start_IO. Tested with python 3.10 (I think there was some change in the past regarding threads and async)
import asyncio
import threading
from asyncio.queues import Queue
import time
class Client:
def __init__(self):
self.streamQ = Queue()
self.loop = None
self.start_IOLoop_thread()
self.stream_listener()
def stream_listener(self):
self.streaming = True
async def listener():
print("Listener Running")
while self.streaming:
data = await self.streamQ.get()
# DEBUG
print(data)
print("Listener Stopped")
print("Starting Listener")
self.listener = asyncio.run_coroutine_threadsafe(listener(), self.loop)
def start_IOLoop_thread(self):
async def inf_loop():
# Keep the main thread alive and doing nothing
# so we can freely give it tasks as needed
while True:
await asyncio.sleep(1)
async def main():
await inf_loop()
def start_IO():
self.loop = asyncio.new_event_loop()
self.loop.create_task(main())
asyncio.set_event_loop(self.loop)
self.loop.run_forever()
print("Main Exited")
threading.Thread(target=start_IO, daemon=True).start()
# A small delay is needed to give the loop time to initialize,
# otherwise self.loop is passed as "None"
time.sleep(0.1)
if __name__ == "__main__":
C = Client()
input("Enter to exit")
I want to combine a TelegramBot with a PYQT application. I'm using telethon to listen to a channel and print out messages. However, this should run in the background, that is, the user should be able to interact with the GUI while the bot is listening to messages. So far, I've only managed to implement a blocking variant. That is, the gui is not showing because with client seems to be blocking.
class Win(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi("layout.ui", self)
client = TelegramClient('session', TELE_ID, TELE_HASH)
#client.on(events.NewMessage(chats=["test"]))
async def m_listener(event):
print(event)
with client:
client.run_until_disconnected()
I also tried to move the last statement to a thread with
...
def run():
with client:
client.run_until_disconnected()
threading.Thread(target=run).start()
However, then I get a There is no current event loop in thread 'Thread-1'. error because with client tries to load the loop from asyncio which can only be done in the main thread.
Do you can give me any starting point on how to solve this?
It needs to create new async loop inside thread - so it needs to create new Client also in thread.
import asyncion
def run_bot():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
#client = TelegramClient('session', TELE_ID, TELE_HASH, loop=loop)
client = TelegramClient('session', TELE_ID, TELE_HASH) # also works
# ... code ...
with client:
client.run_until_disconnected()
# ---
threading.Thread(target=run_bot).start()
Working example which also use Queue (queue_in) to send command to bot.
It has also queue_out to send message from bot to PyQt but I didn't create code which could use this message.
PyQt has own class QThread which could be more useful because it uses singals to send messages.
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton
from PyQt5.QtCore import Qt
#from PyQt5.QtCore import QThread
from telethon.sync import TelegramClient, events
import asyncio
from queue import Queue
from threading import Thread
import os
TOKEN = os.getenv('TELEGRAM_TOKEN')
TELE_ID, TELE_HASH = TOKEN.split(":")
# --- classes ---
class MyWindow(QWidget):
def __init__(self, queue_in, queue_out):
super().__init__()
self.queue_in = queue_in
self.queue_out = queue_out
self.label = QLabel(self, text='Hello World', alignment=Qt.AlignCenter)
self.button = QPushButton(self, text='Stop Bot')
self.button.clicked.connect(self.stop_bot)
vbox = QVBoxLayout()
vbox.addWidget(self.label)
vbox.addWidget(self.button)
self.setLayout(vbox)
def stop_bot(self):
self.queue_in.put('stop')
# --- functions ---
def run_bot(queue_in, queue_out):
print("run_bot()")
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
print('loop:', loop)
client = TelegramClient('session', TELE_ID, TELE_HASH)#, loop=loop)
#client.on(events.NewMessage(pattern='/test'))
async def test_cmd(event):
#print('[BOT] event:', event)
print('[BOT] message:', event.message.message)
queue_out.put(event.message.message)
#client.on(events.NewMessage(pattern='/stop'))
async def stop_cmd(event):
print('[BOT] message:', event.message.message)
queue_out.put(event.message.message)
await client.disconnect() # it stops client
async def check_queue():
print('[BOT] check_queue(): start')
while True:
await asyncio.sleep(1)
#print('[BOT] check_queue(): check')
if not queue_in.empty():
cmd = queue_in.get()
print('[BOT] check_queue(): queue_in get:', cmd)
if cmd == 'stop':
await client.disconnect()
break
loop.create_task(check_queue())
with client:
print('[BOT] start')
client.run_until_disconnected()
print('[BOT] stop')
# --- main ---
queue_in = Queue() # to send data to bot
queue_out = Queue() # to receive data from bot
thread = Thread(target=run_bot, args=(queue_in, queue_out))
thread.start()
# - GUI -
print('PyQt')
app = QApplication([])
win = MyWindow(queue_in, queue_out)
win.show()
app.exec()
queue_in.put('stop')
thread.join()
I am trying to run a simple aiohttp server that can be stopped by the user hitting CTRL+C.
However, right now it appears to stop immediately with no error messages after starting. What might be the problem?
import asyncio
from aiohttp import web
class Foo:
def __init__(self):
self.app = web.Application()
self.app.add_routes([])
self.runner = web.AppRunner(self.app)
async def start(self):
await self.runner.setup()
site = web.TCPSite(self.runner, "127.0.0.1", 1234)
await site.start()
async def stop(self):
await self.runner.cleanup()
async def main():
await foo.start()
if __name__ == '__main__':
foo = Foo()
try:
asyncio.run(main())
except KeyboardInterrupt:
asyncio.run(foo.stop())
I am running a python file from a QProcess in a PyQt5 GUI.
I am showing everything from the terminal using readAllStandardOutput(I tried readAll also) to a textBox
In the python file, I am using telethon (library for using telegram API) python file is having a async event-handler in which I am printing something.
But before starting the starting the client readAllStandardOutput is reading fine. BUt after that print("Sent Hi!") is neither showing on the terminal nor Qprocess is reading it.
Pyqt5 GUI file having QProcess declaration.
class MainWindow(QMainWindow):
def __init__(self,parent = None):
super(QMainWindow, self).__init__(parent)
self.process = QProcess(self)
self.process.readyReadStandardOutput.connect(self.stdoutReady)
self.process.readyReadStandardError.connect(self.stderrReady)
self.process.started.connect(lambda: print('Started!'))
self.process.finished.connect(lambda: print('Finished!'))
self.process.setProcessChannelMode(QProcess.MergedChannels)
def startMain(self):
self.process.start('python3', ['mainApp2.py'])
def stdoutReady(self):
text = str(self.process.readAllStandardOutput())
self.mainTextBox.setText(str(text.strip()))
The python file QProess will start
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
print("Main App started")
api_id = #api_id integer
api_hash = "api_hash string"
client = TelegramClient('session', api_id, api_hash , loop = loop )
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
#client.on(events.NewMessage)
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('Hi!')
await say_after(1, 'Sent Hi!')
client.start()
client.run_until_disconnected()
"Main app Started" is displayed on the text box but not "Sent Hi!".
And the event.reply('Hi!') is working fine.
I am not sure may be it is because it is creating a child process
I want to redirect all output from the terminal to the textBox in the GUI (if you have other methods do suggest).
I am currently developing a server program in Python that uses the websockets and asyncio packages.
I got a basic script handling websockets working (Exhibit A). This script locks when waiting for input, which is not what I want.
The solution for this that I imagine is I can start two asynchronous tasks - one that handles inputs and one that handles outputs - and start them in a secondary event loop. I had to do some research about coroutines, and I came up with Exhibit B as a proof of concept for running two things simultaneously in an event loop.
Now what I'm stuck on is Exhibit C. When I attempted to use this in a practical scenario with the websockets package, I found that websocket.recv() never finishes (or the coroutine never un-pauses - I'm not sure what's going on exactly). In exhibit A it works fine, and I've determined that the coroutine definitely runs at least up until that point.
Any ideas?
Exhibit A:
#!/usr/bin/python3
import asyncio
import websockets
import time
# This works great!
async def hello(websocket, path):
while True:
# This line waits for input from socket
name = await websocket.recv()
print("< {}".format(name))
# "echo... echo... echo... echo... echo..."
greeting = ''.join(name + "... " for x in range(5))
await websocket.send(greeting)
print("> {}".format(greeting))
time.sleep(0.1);
start_server = websockets.serve(hello, '', 26231)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Exhibit B:
#!/usr/bin/python3
import asyncio
import time
class Thing:
def __init__(self):
pass
async def test(self):
for x in range(10):
print("Hello"+str(x))
await asyncio.sleep(0)
def run(self):
# Add the task to the event loop twice
asyncio.ensure_future(self.test())
asyncio.ensure_future(self.test())
t = Thing()
t.run()
loop = asyncio.get_event_loop();
loop.run_forever()
Exhibit C:
#!/usr/bin/python3
import asyncio
import websockets
import time
class WebsocketRequest:
def __init__(self, websocket):
self.websocket = websocket
# Works great
async def handle_oputs(self):
# This works fine - sends a message
# every 10 seconds to the client
while True:
print("sending...")
await self.websocket.send("Hello")
print("> {}".format("Hello"))
time.sleep(10)
# Doesn't work
async def handle_iputs(self):
# This stops at the await and never receives
# any input from the client :/
while True:
try:
print("receiving...")
# This is the line that causes sadness
data = await self.websocket.recv()
print("< {}".format(data))
except:
# This doesn't happen either
print("Listener is dead")
async def run(self):
# This is the part where the coroutine for
# a client get split off into two of them
# to handle input and output separately.
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
asyncio.ensure_future(self.handle_iputs())
asyncio.ensure_future(self.handle_oputs())
loop.run_forever()
class WebsocketServer:
def __init__(self, address):
self.ip = address[0]
self.port = address[1]
async def hello(self, websocket, path):
req = WebsocketRequest(websocket)
await req.run()
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
start_server = websockets.serve(self.hello, self.ip, self.port)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Maybe the module websocket (unlike websockets) can help you.
The use of WebsocketApp is very simple:
from websocket import WebSocketApp
class ExampleClass(object):
def __init__(self):
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp("ws://echo.websocket.org",
on_message=on_message,
on_error=on_error,
on_close=on_close)
def on_message(ws, msg):
print "Message Arrived:" + msg
def on_error(ws, error):
print error
def on_close(ws):
print "Connection Closed"
def on_open(ws):
ws.send("Hello!")
To download this module: https://pypi.python.org/pypi/websocket-client
time.sleep() is a blocking operation, so any other tasks cannot interrupt and are not scheduled. Use await asyncio.sleep() instead.