I tried to learn how asnychron programming in python works and wrote a small tornado app which executues two asnyc loops with sleep commands.
If I wait for both coroutines with two await commands, it behaves as expected (first loop, than the second loop is executed.)
If I combine both coroutines with gather, nothing happens. (No errors, no print output, the webrequest is never finished.)
I don't understand what is happening with *await gather(coros, return_exceptions=True),
from asyncio import gather
import os.path
import tornado.ioloop
from tornado.options import define, options, parse_command_line
import tornado.web
import tornado.platform.asyncio
from tornado.gen import sleep
import datetime;
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=False, help="run in debug mode")
class AsyncTestHandler(tornado.web.RequestHandler):
async def get(self):
operation = self.get_query_argument('operation')
if operation == 'with_two_waits':
await self._with_two_waits()
elif operation == 'with_gather':
await self._with_gather()
else:
self.finish('use operation=with_two_waits or operation=with_gather')
return
self.finish('finished ' + operation);
async def _with_two_waits(self):
print('_with_two_waits: start' + str(datetime.datetime.now()) )
w1 = self._wait_loop("First loop", 8)
w2 = self._wait_loop("Second loop", 6)
await w1
await w2
print('_with_two_waits: finished' + str(datetime.datetime.now()))
async def _with_gather(self):
print('_with_gather: start' + str(datetime.datetime.now()))
coros = []
coros.append(self._wait_loop("First loop", 8))
coros.append(self._wait_loop("Second loop", 6))
await gather(*coros, return_exceptions=True)
print ('_with_gather: finished' + str(datetime.datetime.now()))
async def _wait_loop(self, loop_name, count):
for i in range(1, count + 1):
print(loop_name + ' ' + str(i) + '/' + str(count) + ' ' + str(datetime.datetime.now()))
await sleep(0.1)
print(loop_name + ' complete')
def start_web_app():
parse_command_line()
app = tornado.web.Application(
[
(r"/asnycTest", AsyncTestHandler),
],
debug=options.debug,
)
app.listen(options.port)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
start_web_app()
gather uses the asyncio event loop. If you wish to mix asyncio with tornado, you need to install tornado's asyncio event loop:
Add this to your imports:
from tornado.platform.asyncio import AsyncIOMainLoop
Remove this:
import tornado.platform.asyncio
And add this line right after the imports:
AsyncIOMainLoop().install()
Related
I've inherited some code that utilizes Python Bleak to scan for advertisements emitted from a certain device. Whenever an advertisement from the Bluetooth mac address and service id we're looking for is detected and a certain condition from the extracted payload information is true, we want to terminate and return. In the attached code, I've masked the Bluetooth and service ID:s.
Not being too familiar with the event loop, is there a way to exit before the timer runs out? I suppose there's probably a better way to approach this problem.
Sample code:
import asyncio
import struct
from bleak import BleakScanner
timeout_seconds = 10
address_to_look_for = 'masked'
service_id_to_look_for = 'masked'
def detection_callback(device, advertisement_data):
if device.address == address_to_look_for:
byte_data = advertisement_data.service_data.get(service_id_to_look_for)
num_to_test = struct.unpack_from('<I', byte_data, 0)
if num_to_test == 1:
print('here we want to terminate')
async def run():
scanner = BleakScanner()
scanner.register_detection_callback(detection_callback)
await scanner.start()
await asyncio.sleep(timeout_seconds)
await scanner.stop()
if __name__=='__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
I'm sure there are many ways this can be done. A small mod to your code would be rather than having the asyncio.sleep for the full period before you stop the scan, you could could have a while loop that ends on time elapsed or device found event.
For example:
import asyncio
import struct
from bleak import BleakScanner
timeout_seconds = 20
address_to_look_for = 'F1:D9:3B:39:4D:A2'
service_id_to_look_for = '0000feaa-0000-1000-8000-00805f9b34fb'
class MyScanner:
def __init__(self):
self._scanner = BleakScanner()
self._scanner.register_detection_callback(self.detection_callback)
self.scanning = asyncio.Event()
def detection_callback(self, device, advertisement_data):
# Looking for:
# AdvertisementData(service_data={
# '0000feaa-0000-1000-8000-00805f9b34fb': b'\x00\xf6\x00\x00\x00Jupiter\x00\x00\x00\x00\x00\x0b'},
# service_uuids=['0000feaa-0000-1000-8000-00805f9b34fb'])
if device.address == address_to_look_for:
byte_data = advertisement_data.service_data.get(service_id_to_look_for)
num_to_test, = struct.unpack_from('<I', byte_data, 0)
if num_to_test == 62976:
print('\t\tDevice found so we terminate')
self.scanning.clear()
async def run(self):
await self._scanner.start()
self.scanning.set()
end_time = loop.time() + timeout_seconds
while self.scanning.is_set():
if loop.time() > end_time:
self.scanning.clear()
print('\t\tScan has timed out so we terminate')
await asyncio.sleep(0.1)
await self._scanner.stop()
if __name__ == '__main__':
my_scanner = MyScanner()
loop = asyncio.get_event_loop()
loop.run_until_complete(my_scanner.run())
Hi I'm trying to leave a channel with id with this script but it not showing any results well LeaveChannelRequest doesn't worked for me too
here is what i tried:
import asyncio
import logging
import re
import time
import os
import sys
import requests
logging.basicConfig(level=logging.ERROR)
from telethon import TelegramClient, events
from telethon.tl.functions.channels import LeaveChannelRequest
from telethon.tl.functions.channels import JoinChannelRequest
from telethon.tl.functions.messages import GetBotCallbackAnswerRequest
from datetime import datetime
from colorama import Fore, init as color_ama
color_ama(autoreset=True)
os.system('cls' if os.name=='nt' else 'clear')
# my.telegram.org values, get your own there
api_id = ''
api_hash = ''
input_channel = '-1001226909513'
def print_msg_time(message):
print('[' + Fore.YELLOW + f'{datetime.now().strftime("%H:%M:%S")}' + Fore.RESET + f'] {message}')
async def main():
if len(sys.argv) < 2:
print('Usage: python start.py phone_number')
print('-> Input number in international format (example: +10123456789)\n')
e = input('Press any key to exit...')
exit(1)
phone_number = sys.argv[1]
if not os.path.exists("session"):
os.mkdir("session")
client = TelegramClient('session/' + phone_number, api_id, api_hash)
await client.start(phone_number)
me = await client.get_me()
print(Fore.GREEN + f' Current account: {me.first_name}({me.username})\n' + Fore.RESET)
print_msg_time('leaving channels')
async def main():
await client.delete_dialog(input_channel)
print_msg_time(f'{input_channel} has been leaved')
asyncio.get_event_loop().run_until_complete(main())
where is my problem?
The method call to leave the channel or group is defined in inner function
[...]
print_msg_time('leaving channels')
async def main():
await client.delete_dialog(input_channel)
print_msg_time(f'{input_channel} has been leaved')
which isn't called in outer function, you could avoid using a inner function. for example, you can call delete_dialog method like this
[...]
print_msg_time('leaving channels')
await client.delete_dialog(input_channel)
print_msg_time(f'{input_channel} has been leaved')
I have a set of CPU-intensive processes that once in a while depend on each other to proceed. So something like
def run():
while True:
do stuff
wake up some other process
wait for some other process to wake me up
do stuff
Within each process I'd like to use async, so that I can always have an instance of run running while others are waiting to be woken up. Looking at the asyncio docs, the only IPC option in the "High-level APIs" section that I see uses sockets. I'd much rather use a pipe, which it looks like I can perhaps do with the low-level API, but that documentation is chock full of warnings that if you're just writing an application then it's a mistake to be using it. Can someone weigh in on the idiomatic thing to do here? (And also, speed is an important factor, so if there's some less-idiomatic-but-more-performant thing I'd like to know about that option as well.)
I would like to mention the aioprocessing library, as I successfully used it in one of my projects. It provides an anync interface to the multiprocessing primitives including IPC, such as Process, Pipe, Lock, Queue and etc. It uses thread pool to do this:
...
#staticmethod
def coro_maker(func):
def coro_func(self, *args, loop=None, **kwargs):
return self.run_in_executor(
getattr(self, func), *args, loop=loop, **kwargs
)
return coro_func
But to be honest, a lot depends on the problem being solved, on what tasks are being performed concurrently, since the intensive IPC itself within the async approach is less effective than the synchronous approach due to overhead of event loop, thread pool and etc. Sometimes it is better to make all IPC operations synchronous and put it all in a separate thread. Again, it all depends on the problem and the environment. Below is a benchmark that is far from comprehensive, but it can give an approximate picture of the problem that is being solved in it (intensive exchange of buffers).
note: I wrote about the difference between a Queue and SimpleQueue here
Sync SimpleQueue: 1.4309470653533936
AioSimpleQueue: 12.32670259475708
AioQueue: 14.342737436294556
AioPipe: 11.747064590454102
subprocess pipe stream: 7.344956159591675
socket stream: 4.360717058181763
# main.py
import sys
import time
import asyncio
import aioprocessing as ap
import multiprocessing as mp
import proc
count = 5*10**4
data = b'*'*100
async def sync_simple_queue_func():
out_ = mp.SimpleQueue()
in_ = mp.SimpleQueue()
p = ap.AioProcess(target=proc.start_sync_queue_func, args=(out_, in_))
p.start()
begin_ts = time.time()
for i in range(count):
out_.put(data)
res = in_.get()
print('Sync SimpleQueue: ', time.time() - begin_ts)
out_.put(None)
async def simple_queue_func():
out_ = ap.AioSimpleQueue()
in_ = ap.AioSimpleQueue()
p = ap.AioProcess(target=proc.start_queue_func, args=(out_, in_))
p.start()
begin_ts = time.time()
for i in range(count):
await out_.coro_put(data)
res = await in_.coro_get()
print('AioSimpleQueue: ', time.time() - begin_ts)
await out_.coro_put(None)
async def queue_func():
out_ = ap.AioQueue()
in_ = ap.AioQueue()
p = ap.AioProcess(target=proc.start_queue_func, args=(out_, in_))
p.start()
begin_ts = time.time()
for i in range(count):
await out_.coro_put(data)
res = await in_.coro_get()
print('AioQueue: ', time.time() - begin_ts)
await out_.coro_put(None)
async def pipe_func():
main_, child_ = ap.AioPipe()
p = ap.AioProcess(target=proc.start_pipe_func, args=(child_,))
p.start()
begin_ts = time.time()
for i in range(count):
await main_.coro_send(data)
res = await main_.coro_recv()
print('AioPipe: ', time.time() - begin_ts)
await main_.coro_send(None)
await p.coro_join()
server = None
async def handle_child(reader, writer):
begin_ts = time.time()
for i in range(count):
writer.write(data)
res = await reader.read(len(data))
print('socket stream: ', time.time() - begin_ts)
writer.close()
async def socket_func():
global server
addr = ('127.0.0.1', 8888)
server = await asyncio.start_server(handle_child, *addr)
p = ap.AioProcess(target=proc.start_socket_func, args=(addr,))
p.start()
async with server:
await server.serve_forever()
async def subprocess_func():
prog = await asyncio.create_subprocess_shell(
'python proc.py',
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE)
begin_ts = time.time()
for i in range(count):
prog.stdin.write(data)
res = await prog.stdout.read(len(data))
print('subprocess pipe stream: ', time.time() - begin_ts)
prog.stdin.close()
async def main():
await sync_simple_queue_func()
await simple_queue_func()
await queue_func()
await pipe_func()
await subprocess_func()
await socket_func()
asyncio.run(main())
# proc.py
import asyncio
import sys
import aioprocessing as ap
async def sync_queue_func(in_, out_):
while True:
n = in_.get()
if n is None:
return
out_.put(n)
async def queue_func(in_, out_):
while True:
n = await in_.coro_get()
if n is None:
return
await out_.coro_put(n)
async def pipe_func(child):
while True:
n = await child.coro_recv()
if n is None:
return
await child.coro_send(n)
data = b'*' * 100
async def socket_func(addr):
reader, writer = await asyncio.open_connection(*addr)
while True:
n = await reader.read(len(data))
if not n:
break
writer.write(n)
def start_sync_queue_func(in_, out_):
asyncio.run(sync_queue_func(in_, out_))
def start_queue_func(in_, out_):
asyncio.run(queue_func(in_, out_))
def start_pipe_func(child):
asyncio.run(pipe_func(child))
def start_socket_func(addr):
asyncio.run(socket_func(addr))
async def connect_stdin_stdout():
loop = asyncio.get_event_loop()
reader = asyncio.StreamReader()
protocol = asyncio.StreamReaderProtocol(reader)
dummy = asyncio.Protocol()
await loop.connect_read_pipe(lambda: protocol, sys.stdin) # sets read_transport
w_transport, _ = await loop.connect_write_pipe(lambda: dummy, sys.stdout)
writer = asyncio.StreamWriter(w_transport, protocol, reader, loop)
return reader, writer
async def main():
reader, writer = await connect_stdin_stdout()
while True:
res = await reader.read(len(data))
if not res:
break
writer.write(res)
if __name__ == "__main__":
asyncio.run(main())
I need to execute code inside while loop every x seconds without stoping loop work
I have trying threading and lock combinations but it is still not working. I am working on python 3.7.4, pycharm 2019.2
#!/usr/bin/env python3
import configparser
import logging
import threading
import time
import ts3
__all__ = ["notify_bot"]
logging.basicConfig(filename='ts3bot.log',
level=logging.INFO,
format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s",
)
logging.getLogger().addHandler(logging.StreamHandler())
def notify_bot(ts3conn, config, lock):
logging.info("Start Notify Bot ...")
lock.acquire()
ts3conn.exec_("servernotifyregister", event="server")
lock.release()
while True:
event = ts3conn.wait_for_event()
try:
reasonid_ = event[0]["reasonid"]
except KeyError:
continue
if reasonid_ == "0":
logging.info("User joined Lobby:")
logging.info(event[0])
servergroups = event[0]['client_servergroups']
guestname = event[0]['client_nickname']
lock.acquire()
if not set(servergroups):
print(f"s1 {guestname}")
else:
print(f"s2{guestname}")
lock.release()
return None
def keep_alive(ts3conn, lock):
while True:
logging.info("Send keep alive!")
lock.acquire()
ts3conn.send_keepalive()
lock.release()
time.sleep(5)
if __name__ == "__main__":
logging.info("Start TS Bot ...")
config = configparser.ConfigParser()
config.sections()
config.read("settings_test.ini")
logging.info("Config loaded!")
HOST = config['server']['url']
PORT = config['server']['query_port']
USER = config['server']['query_user']
PASS = config['server']['query_pw']
SID = config['server']['sid']
NAME = config['bot']['name']
logging.info("Connecting to query interface ...")
URI = f"telnet://{USER}:{PASS}#{HOST}:{PORT}"
try:
with ts3.query.TS3ServerConnection(URI) as ts3conn:
ts3conn.exec_("use", sid=SID)
ts3conn.query("clientupdate", client_nickname="x123d")
logging.info("Connected!")
lock = threading.Lock()
notify_thread = threading.Thread(target=notify_bot, args=(ts3conn, config, lock), daemon=True,
name="notify")
keep_alive_thread = threading.Thread(target=keep_alive, args=(ts3conn, lock), daemon=True,
name="keep_alive")
notify_thread.start()
keep_alive_thread.start()
keep_alive_thread.join()
notify_thread.join()
except KeyboardInterrupt:
logging.INFO(60 * "=")
logging.info("TS Bot terminated by user!")
logging.INFO(60 * "=")
After run work for 1 person who join server and do nothing, dont send keep alive and dont work at all
you can use Bibio TIME
You can check it from official python website (https://docs.python.org/3/library/time.html)
Personally, for simple things, I find the _thread library easier. Here's a function that you can run in a thread, and an example of starting that thread:
import _thread
def mythread(arg1):
while True:
time.sleep(arg1)
do.whatever()
_thread.start_new_thread(mythread, (5,))
The important thing to note is the second argument I passed to the _thread.start_new_thread function. It must be a tuple, which is why there is a comma after the 5. Even if your function doesn't require any arguments, you have to pass a tuple.
I am using time module and threading,
I'v made some changes and it seems to work
#!/usr/bin/env python3
import configparser
import logging
import threading
import time
import ts3
logging.basicConfig(filename='ts3bot.log',
level=logging.INFO,
format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s",
)
logging.getLogger().addHandler(logging.StreamHandler())
def notify_bot(ts3conn):
logging.info("Start Notify Bot ...")
ts3conn.exec_("servernotifyregister", event="server")
while True:
event = ts3conn.wait_for_event()
try:
reasonid_ = event[0]["reasonid"]
except KeyError:
continue
if reasonid_ == "0":
logging.info("User joined Lobby:")
logging.info(event[0])
servergroups = event[0]['client_servergroups']
guestname = event[0]['client_nickname']
if not set(servergroups):
print(f"s1 {guestname}")
else:
print(f"s2{guestname}")
return None
def keep_alive(ts3conn, time):
while True:
logging.info("Send keep alive!")
ts3conn.send_keepalive()
time.sleep(20)
if __name__ == "__main__":
logging.info("Start TS Bot ...")
config = configparser.ConfigParser()
config.sections()
config.read("settings_test.ini")
logging.info("Config loaded!")
HOST = config['server']['url']
PORT = config['server']['query_port']
USER = config['server']['query_user']
PASS = config['server']['query_pw']
SID = config['server']['sid']
NAME = config['bot']['name']
logging.info("Connecting to query interface ...")
URI = f"telnet://{USER}:{PASS}#{HOST}:{PORT}"
try:
with ts3.query.TS3ServerConnection(URI) as ts3conn:
ts3conn.exec_("use", sid=SID)
ts3conn.query("clientupdate", client_nickname="x123d")
logging.info("Connected!")
notify_thread = threading.Thread(target=notify_bot, args=(ts3conn,), daemon=True,
name="notify")
keep_alive_thread = threading.Thread(target=keep_alive, args=(ts3conn, time), daemon=True,
name="keep_alive")
notify_thread.start()
keep_alive_thread.start()
keep_alive_thread.join()
notify_thread.join()
except KeyboardInterrupt:
logging.INFO(60 * "=")
logging.info("TS Bot terminated by user!")
logging.INFO(60 * "=")
It looks like ts3conn.send_keepalive() making error, when I delete it, code work fine, when I'v add it, code stop working after send ts3conn.send_keepalive() once
I have a websocket server (python 3.x) taking requests where each is a url variable. It runs just fine except it only executes each request in serial, after one another. While the function is running it also blocks the client(s) trying to connect. Non-blocking is what i want!
Asyncronous multiprocessed threading of both websocket and subprocess function.
The ability to set the number of cores to use. This is not obligatory though.
Here's what i've got:
ANSWER (illustration and asyncio.subprocess in accepted answer)
So, I didn't get very far with this frustration. I reverted back to my original code and as it turns out, you need to sleep the function with await asyncio.sleep(.001). Now it runs perfectly fine, I tested with multiple clients at the same time and it handles it asynchronously.
import asyncio, websockets, json
async def handler(websocket, path):
print("New client connected.")
await websocket.send('CONNECTED')
try:
while True:
inbound = await websocket.recv()
if inbound is None:
break
while inbound != None:
import time
for line in range(10):
time.sleep(1)
data = {}
data['blah'] = line
await asyncio.sleep(.000001) # THIS
print(data)
await websocket.send(json.dumps(data))
await websocket.send(json.dumps({'progress': 'DONE'}))
break
except websockets.exceptions.ConnectionClosed:
print("Client disconnected.")
if __name__ == "__main__":
server = websockets.serve(handler, '0.0.0.0', 8080)
loop = asyncio.get_event_loop()
loop.run_until_complete(server)
loop.run_forever()
Update: as suggested by #udi, if you want a slow external process, the way to go is asyncio.subprocess and not subprocess. Reading from pipe with a blocking call stalls the other threads, which is what asyncio.subprocess takes care of.
time.sleep() is blocking.
Try:
# blocking_server.py
import asyncio
import time
import websockets
x = 0
async def handler(websocket, path):
global x
x += 1
client_id = x
try:
print("[#{}] Connected.".format(client_id))
n = int(await websocket.recv())
print("[#{}] Got: {}".format(client_id, n))
for i in range(1, n + 1):
print("[#{}] zzz...".format(client_id))
time.sleep(1)
print("[#{}] woke up!".format(client_id))
await asyncio.sleep(.001)
msg = "*" * i
print("[#{}] sending: {}".format(client_id, msg))
await websocket.send(msg)
msg = "bye!"
print("[#{}] sending: {}".format(client_id, msg))
await websocket.send(msg)
print("[#{}] Done.".format(client_id, msg))
except websockets.exceptions.ConnectionClosed:
print("[#{}] Disconnected!.".format(client_id))
if __name__ == "__main__":
port = 8080
server = websockets.serve(handler, '0.0.0.0', port)
print("Started server on port {}".format(port))
loop = asyncio.get_event_loop()
loop.run_until_complete(server)
loop.run_forever()
With this test client:
# test_client.py
import asyncio
import time
import websockets
async def client(client_id, n):
t0 = time.time()
async with websockets.connect('ws://localhost:8080') as websocket:
print("[#{}] > {}".format(client_id, n))
await websocket.send(str(n))
while True:
resp = await websocket.recv()
print("[#{}] < {}".format(client_id, resp))
if resp == "bye!":
break
print("[#{}] Done in {:.2f} seconds".format(client_id, time.time() - t0))
tasks = [client(i + 1, 3) for i in range(4)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
Now compare the result when time.sleep(x) is replaced with await asyncio.sleep(x)!
If you need to run a slow external process via asyncio, try asynico.subprocess:
An example external program:
# I am `slow_writer.py`
import sys
import time
n = int(sys.argv[1])
for i in range(1, n + 1):
time.sleep(1)
print("*" * i)
with this server:
# nonblocking_server.py
import asyncio
import sys
import websockets
x = 0
async def handler(websocket, path):
global x
x += 1
client_id = x
try:
print("[#{}] Connected.".format(client_id))
n = int(await websocket.recv())
print("[#{}] Got: {}. Running subprocess..".format(client_id, n))
cmd = (sys.executable, 'slow_writer.py', str(n))
proc = await asyncio.create_subprocess_exec(
*cmd, stdout=asyncio.subprocess.PIPE)
async for data in proc.stdout:
print("[#{}] got from subprocess, sending: {}".format(
client_id, data))
await websocket.send(data.decode().strip())
return_value = await proc.wait()
print("[#{}] Subprocess done.".format(client_id))
msg = "bye!"
print("[#{}] sending: {}".format(client_id, msg))
await websocket.send(msg)
print("[#{}] Done.".format(client_id, msg))
except websockets.exceptions.ConnectionClosed:
print("[#{}] Disconnected!.".format(client_id))
if __name__ == "__main__":
if sys.platform == 'win32':
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
port = 8080
server = websockets.serve(handler, '0.0.0.0', port)
print("Started server on port {}".format(port))
loop = asyncio.get_event_loop()
loop.run_until_complete(server)
loop.run_forever()