timeout context manager should be used inside a task aiogram - multithreading

async def check():
print("Check...")
while 1:
if ...
await bot.send_message(...)
def startup():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(check())
loop.close()
async def on_startup(dp):
threading.Thread(target=startup).start()
I'm running an async function in a new thread. As a result, when it is necessary to send a message, an error occurs:
timeout context manager should be used inside a task
what to do?

Related

Best way to avoid warning about un-run coroutines that are not-yet run by cancelled tasks?

In the following, the coroutinerunIt() is created and provided as a parameter to delegate(...) - which is turned into a Task that is canceled before runIt executes:
import asyncio
async def cancelTaskTest():
async def runIt():
print("RunIt ran")
async def delegate(coro):
await coro
task = asyncio.create_task(delegate(runIt()))
task.cancel()
if __name__=='__main__':
asyncio.run(cancelTaskTest())
Produces the unwanted warning:
/usr/lib/python3.10/asyncio/base_events.py:1881: RuntimeWarning: coroutine 'cancelTaskTest.<locals>.runIt' was never awaited
handle = self._ready.popleft()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
I'm aware that runIt did not run. I don't want a warning about it - what's the best way to avoid this.
Simplest method would be to remove () in runIt and call it in runAfterTimeout():
import asyncio
async def cancelTaskTest():
async def runIt():
print("RunIt ran")
async def delegate(asyncFunc):
coro = asyncFunc() # <-- put () here
await coro
task = asyncio.create_task(delegate(runIt)) # <-- removed () in runIt
task.cancel()
if __name__ == "__main__":
asyncio.run(cancelTaskTest())
EDIT: To add parameters to RunIt, just create plain lambda::
import asyncio
async def cancelTaskTest():
async def runIt(p1, p2):
print(f"RunIt({p1}, {p2})")
async def delegate(coro):
await coro()
task = asyncio.create_task(delegate(lambda: runIt(1, 2)))
task.cancel()
if __name__ == "__main__":
asyncio.run(cancelTaskTest())

wait in separate thread without blocking main eventloop, async python

I have a problem at work where I have to wait for 10 seconds when InstrInstallSucceeded event comes in, without blocking the main thread, I should wait for InstrInstallFailed to appear, so in other words 'ToolOn', 'ToolOn', 'ToolOn' should appear without any wait.
import asyncio
from threading import Thread
import time
FLAG = True
async def sleep_loop(t, event):
global FLAG
print(event)
if event == 'InstrInstallSucceeded':
# spwan a seperate thread here such that
# toolon events are not blocked by the sleep
await asyncio.sleep(t)
FLAG = True
if event == 'InstrInstallFailed':
# and I want to update the FLAG whenever I see event == 'InstrInstallFailed'
FLAG = False
async def keep_print():
print(f'Beginning FLAG:: {FLAG}')
while FLAG:
pass
print(f'End FLAG:: {FLAG}')
def start_loop(loop, t):
print("in start loop")
asyncio.set_event_loop(loop)
for i in ['InstrInstallSucceeded', 'ToolOn','ToolOn', 'ToolOn', 'InstrInstallFailed']:
loop.run_until_complete(asyncio.sleep(1))
loop.run_until_complete(sleep_loop(t, i))
loop = asyncio.get_event_loop()
new_loop = asyncio.new_event_loop()
t = Thread(target=start_loop, args=(new_loop,10))
t.start()
coro = keep_print()
loop.run_until_complete(coro)
output
in start loop
Beginning FLAG:: True
Executing <Task pending coro=<sleep() running at /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/tasks.py:482> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1043f2be8>()] created at /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py:284> cb=[_run_until_complete_cb() at /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py:185] created at /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py:452> took 0.118 seconds
InstrInstallSucceeded
ToolOn
ToolOn
ToolOn
InstrInstallFailed
End FLAG:: False
Executing <Task finished coro=<keep_print() done, defined at fut.py:21> result=None created at /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py:452> took 15.756 seconds
EDIT: using python 3.6.7
import asyncio
async def dispatch_event(event, alert):
print(event)
if event == 'InstrInstallSucceeded':
# spawn a coroutine if you need something done in parallel
#asyncio.create_task(xxx())
await asyncio.sleep(10)
if event == 'InstrInstallFailed':
await asyncio.sleep(.5)
# alert the watcher(s) of the event that was dispatched
alert.last_event = event
alert.set()
async def keep_print(alert):
while True:
print(f'Beginning FLAG:: {alert.last_event}')
await alert.wait()
alert.clear()
print(f'End FLAG:: {alert.last_event}')
async def main():
alert = asyncio.Event()
alert.last_event = None
# spawn keep_print in the "background"
loop = asyncio.get_event_loop()
t = loop.create_task(keep_print(alert))
for i in ['InstrInstallSucceeded', 'ToolOn','ToolOn', 'ToolOn', 'InstrInstallFailed']:
await asyncio.sleep(1)
await dispatch_event(i, alert)
await asyncio.sleep(1)
t.cancel()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
edit as suggested by #user418.....
async def dispatch_event(event,alert):
alert.last_event = event
alert.set()
print(event)
if event == 'InstrInstallSucceeded':
# spawn a coroutine if you need something done in parallel
#asyncio.create_task(xxx())
await asyncio.sleep(10)
if event == 'InstrInstallFailed':
await asyncio.sleep(.5)
# alert the watcher(s) of the event that was dispatched
Threads and asyncio don't go together, except in specific circumstances (e.g. the implementation of run_in_executor). Instead of spawning new threads, spawn new coroutines.
For example:
import asyncio
async def dispatch_event(event, alert):
print(event)
if event == 'InstrInstallSucceeded':
# spawn a coroutine if you need something done in parallel
#asyncio.create_task(xxx())
await asyncio.sleep(1)
if event == 'InstrInstallFailed':
await asyncio.sleep(.5)
# alert the watcher(s) of the event that was dispatched
alert.last_event = event
alert.set()
async def keep_print(alert):
while True:
print(f'Beginning FLAG:: {alert.last_event}')
await alert.wait()
alert.clear()
print(f'End FLAG:: {alert.last_event}')
async def main():
alert = asyncio.Event()
alert.last_event = None
# spawn keep_print in the "background"
t = asyncio.create_task(keep_print(alert))
for i in ['InstrInstallSucceeded', 'ToolOn','ToolOn', 'ToolOn', 'InstrInstallFailed']:
await asyncio.sleep(1)
await dispatch_event(i, alert)
await asyncio.sleep(1)
t.cancel()
asyncio.run(main())

run async db read task in background

I have function called get_active_allowed_systems_by_poll where I want to call it in the background 10 times/minute and refresh new systems that I have gotten in last 10 seconds.
import asyncio
from threading import Thread
async def create_state_machine_at_init(app):
worker_loop = asyncio.new_event_loop()
worker = Thread(target=start_db_worker, args=(worker_loop,))
worker.start()
worker_loop.call_soon_threadsafe(get_active_allowed_systems_by_poll, app, 30)
async def get_active_allowed_systems_by_poll(app, interval=10):
params = {
param: key
for param, key
in app.config.get_active_allowed_systems_by_poll_params.items()
}
params['interval'] = interval
operation = prepare_exec(
app.config.get_active_allowed_systems_by_poll,
**params
)
ACTIVE_ALLOWED_SYSTEMS
ACTIVE_ALLOWED_SYSTEMS = (await app['database'].execute(operation)).all()
return ACTIVE_ALLOWED_SYSTEMS
def start_db_worker(loop):
"""Switch to new event loop and run forever"""
asyncio.set_event_loop(loop)
loop.run_forever()
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/events.py:145: RuntimeWarning: coroutine 'get_active_allowed_systems_by_poll' was never awaited

How to Create Custom Events for Python Asyncio

The Mesos scheduler Marathon has an asynchronous HTTP API. E.g. when one deploys an app by posting a JSON to /v2/apps a deployment id is returned. The id can then be used to either poll the deployment state in /v2/deployments or by subscribing to /v2/events and look for the deployment_success event.
I would like to create an asynchronous Python client with coroutines. E.g. client.deploy_app(...) should return once the deployment_success event arrived but not block.
How can I implement these methods with asyncio? How can I create an event listener? It feels an event loop is made for this but I don't see how I register events.
Creating asynchronous post http requests that needed for /v2/apps can be done with aiohttp module:
import asyncio
import aiohttp
async def post(url, json):
async with aiohttp.ClientSession() as session:
async with session.post(url, json=json) as resp:
return await resp.json()
async def main():
res = await post('http://httpbin.org/post', {'test': 'object'})
print(res['json']) # {'test': 'object'}
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
loop.run_until_complete(loop.shutdown_asyncgens())
finally:
loop.close()
If you want to use /v2/events to track deployment success you should request for stream (see api doc). It can be achieved in aiohttp with it's asynchronous iteration: you just asynchronously read content line-by-line waiting for event you need, for example:
import asyncio
import aiohttp
async def stream(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
async for line in resp.content:
yield line
async def main():
async for line in stream('http://httpbin.org/stream/10'):
print(line) # check if line contains event you need
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
loop.run_until_complete(loop.shutdown_asyncgens())
finally:
loop.close()
If you want to use /v2/deployments you should request it periodically waiting some delay using asyncio.sleep. In this case your function won't be blocking:
import asyncio
import aiohttp
async def get(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.json()
async def main():
while True:
# Make request to see if deplayment finished:
res = await get('http://httpbin.org/get')
print(res['origin']) # break if ok here
# Async wait before next try:
await asyncio.sleep(3)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
loop.run_until_complete(loop.shutdown_asyncgens())
finally:
loop.close()

Asyncio, await and infinite loops

async def start(channel):
while True:
m = await client.send_message(channel, "Generating... ")
generator.makeFile()
with open('tmp.png', 'rb') as f:
await client.send_file(channel, f)
await client.delete_message(m)
await asyncio.sleep(2)
I have a discord bot that runs a task every 2 seconds. I tried using an infinite loop for this, but the script crashes with a Task was destroyed but it is still pending! I have read about asyncio's coroutines, but none of the examples that I found use await in them. Is it possible avoid this error, by running a coroutine with await, for example?
Task was destroyed but it is still pending! is warning that you receive when you call loop.close() when some of tasks in your script aren't finished. Usually you should avoid this situation because unfinished task may not release some resources. You need either to await task done or cancel it before event loop closed.
Since you have infinite loop you probably would need to cancel task, example:
import asyncio
from contextlib import suppress
async def start():
# your infinite loop here, for example:
while True:
print('echo')
await asyncio.sleep(1)
async def main():
task = asyncio.Task(start())
# let script some thime to work:
await asyncio.sleep(3)
# cancel task to avoid warning:
task.cancel()
with suppress(asyncio.CancelledError):
await task # await for task cancellation
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(main())
finally:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
See also this answer for more information about tasks.

Resources