Python Asyncio TimeoutError (tasks.loop) - python

I have a script that should run endless. My problem is that after about 10 minutes a timeout error appears.
I tried a try/except, if it is catched, the start method should be called again. But this does not work. The catch work, but the start method cannot be called again.
Here is my code:
#tasks.loop()
async def beginn(self):
print(something)
self.csvToList()
await self.find_price()
def start():
try:
print("run")
mon = monitor()
mon.beginn.start()
client.run(token)
except asyncio.TimeoutError:
print("Timeout")
start()
start()
This is the error message
And for the line numbers

As I see in your code, you wrote client.run(token). So maybe this is discord.py.
I think the best solution to this, in order not to end this loop is this:
#tasks.loop(seconds=1)
async def beginn(self):
print(something)
self.csvToList()
await self.find_price()
#beginn.before_loop
async def before():
await bot.wait_until_ready()
print("Finished waiting")
beginn.start()
bot.run(token)
Do not forget to delete YOUR start() function in order this work properly.

Related

There is a problem in the loop and the second loop does not work

I need a second loop to do something, but no matter what I try, I don't get the desired result :(
code:
#client.event
async def on_ready():
print("Client is online!")
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.playing, name="UwU"))
try:
synced = await client.tree.sync()
print(f"Synced {len(synced)} command(s)")
except Exception as e:
print(e)
while True:
await print("is online! 1")
await asyncio.sleep(3)
await print("is online! 2")
await asyncio.sleep(3)
await print("is online! 3")
await asyncio.sleep(3)
while True:
await print("ready 1")
await asyncio.sleep(3)
await print("ready 2")
await asyncio.sleep(3)
await print("ready 3")
await asyncio.sleep(3)
The code is doing exactly what you have written it to do.
while True: will execute everything in the loop forever until True is not True or until you break out of the loop. If you want to have two loops running simultaneously, we'll have to find another way of running them.
The first option is to use the asyncio library to create tasks.
async def online_loop():
while True:
print("is online")
await asyncio.sleep(3)
async def ready_loop():
while True:
print("is ready")
await asyncio.sleep(3)
# and then in your on_ready function
#client.event
async def on_ready():
asyncio.create_task(online_loop())
asyncio.create_task(ready_loop())
The second option, is to use the tasks functionality built in to discord.py. A very basic implementation would be:
# with your imports
from discord.ext import tasks
#tasks.loop(seconds=3.0)
async def forever_print():
print("is online and ready")
#forever_print.before_loop
async def before_print(self):
print('waiting...')
await client.wait_until_ready()
# then in on_ready
async def on_ready():
forever_print.start()
Again, this is a basic tasks implementation; the docs have more (and better!) examples of how to use them.
Although, I don't really recommend just using loops/tasks just to continuously print something. I like to keep a cleaner log and save printing/logging stuff to console/log file for important messages or debug information rather than just continuously printing that the bot is online/ready every few seconds.

Running asynchronous functions in non-asynchronous functions

I'm trying to run some asynchronous functions in her asynchronous function, the problem is, how did I understand that functions don't run like that, then how do I do it? I don't want to make the maze_move function asynchronous.
async def no_stop():
#some logic
await asyncio.sleep(4)
async def stop(stop_time):
await asyncio.sleep(stop_time)
#some logic
def maze_move():
no_stop()
stop(1.5)
async def main(websocket):
global data_from_client, data_from_server, power_l, power_r
get_params()
get_data_from_server()
get_data_from_client()
while True:
msg = await websocket.recv()
allow_data(msg)
cheker(data_from_client)
data_from_server['IsBrake'] = data_from_client['IsBrake']
data_from_server['powerL'] = power_l
data_from_server['powerR'] = power_r
await websocket.send(json.dumps(data_from_server))
print(data_from_client['IsBrake'])
start_server = websockets.serve(main, 'localhost', 8080)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
How about:
def maze_move():
loop = asyncio.get_event_loop()
loop.run_until_complete(no_stop())
loop.run_until_complete(stop(1.5))
If you wanted to run two coroutines concurrently, then:
def maze_move():
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(no_stop(), stop(1.5)))
Update Based on Updated Question
I am guessing what it is you want to do (see my comment to your question):
First, you cannot call from maze_move coroutines such as stop directly since stop() does not result in calling stop it just returns a coroutine object. So maze_move has to be modified. I will assume you do not want to make it a coroutine itself (why not as long as you already have to modify it?). And further assuming you want to invoke maze_move from a coroutine that wishes to run concurrently other coroutines, then you can create a new coroutine, e.g. maze_move_runner that will run maze_move in a separate thread so that it does not block other concurrently running coroutines:
import asyncio
import concurrent.futures
async def no_stop():
#some logic
print('no stop')
await asyncio.sleep(4)
async def stop(stop_time):
await asyncio.sleep(stop_time)
print('stop')
#some logic
async def some_coroutine():
print('Enter some_coroutine')
await asyncio.sleep(2)
print('Exit some_coroutine')
return 1
def maze_move():
# In case we are being run directly and not in a separate thread:
try:
loop = asyncio.get_running_loop()
except:
# This thread has no current event loop, so:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(no_stop())
loop.run_until_complete(stop(1.5))
return 'Done!'
async def maze_move_runner():
loop = asyncio.get_running_loop()
# Run in another thread:
return await loop.run_in_executor(None, maze_move)
async def main():
loop = asyncio.get_running_loop()
results = await (asyncio.gather(some_coroutine(), maze_move_runner()))
print(results)
asyncio.run(main())
Prints:
Enter some_coroutine
no stop
Exit some_coroutine
stop
[1, 'Done!']
But this would be the most straightforward solution:
async def maze_move():
await no_stop()
await stop(1.5)
return 'Done!'
async def main():
loop = asyncio.get_running_loop()
results = await (asyncio.gather(some_coroutine(), maze_move()))
print(results)
If you have an already running event loop, you can define an async function inside of a sync function and launch it as task:
def maze_move():
async def amaze_move():
await no_stop()
await stop(1.5)
return asyncio.create_task(amaze_move())
This function returns an asyncio.Task object which can be used in an await expression, or not, depending on requirements. This way you won't have to make maze_move itself an async function, although I don't know why that would be a goal. Only a async function can run no_stop and stop, so you've got to have an async function somewhere.

run a function concurrently with client.run() in discord

so i have a discord bot and also another function that i want to run, but i want them to run concurrently but i couldn't make them run without one of them blocking the other, here's an example code
ps: disnake is a fork of dicord.py so it works the same mostly
import disnake
import time
bot = commands.Bot()
async on_message(message):
if message.content == "hello bot":
await message.channel.send("hey user")
x=0
def counting():
while True:
x += 1
print(x)
time.sleep(1)
#here is the issue mostly
bot.run(token)
counting()
i want the bot to run normally, while the other function is running and counting "1" "2" on my terminal, but i can't get to make them both work
Disnake is an async-await library, which means that you should use non-blocking functions. Moreover, the fonctions you want to make concurrent should be async and contain await suspension points in order to let other concurrent routines an opportunity to execute.
The issue is that Bot.run() is a blocking call as the documentation mentions. Moreover your counting() function is not async and does not contain any await. Thus, the two functions cannot run concurrently. Also, you should not use time.sleep() with AsyncIO as it is a blocking operation. You should use asyncio.sleep().
the program could look like this:
import asyncio
import disnake
async on_message(message):
if message.content == "hello bot":
await message.channel.send("hey user")
async def run_bot(bot: Bot, token: str):
try:
await bot.start(token)
except KeyboardInterrupt:
await bot.close()
async def count():
count = 0
while True:
await asyncio.sleep(1.0)
count += 1
print(count)
async def main():
bot = commands.Bot()
token = "<user_token>"
await asyncio.gather(
count(),
run_bot(bot, token)
)
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
Note: you did not seem to use on_message() in your code, which is probably an error.

asyncio print something while waiting for user input

I have a simple script, I want the user to be able to input something whenever he wants, but I want to also print something out in the meantine, this is my code:
import asyncio
async def user_input():
while True:
content = input('> ')
async def print_something():
await asyncio.sleep(10)
print('something')
async def main():
tasks = [user_input(), print_something()]
await asyncio.gather(*tasks)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
It lets me input, but it doesn't print something, how can I achieve that?
input is a blocking function and cannot be used with inside coroutines straightforwardly. But you could start it in a separate thread by means of run_in_executor:
import asyncio
async def user_input():
while True:
loop = asyncio.get_event_loop()
content = await loop.run_in_executor(None, input, "> ")
print(content)
async def print_something():
await asyncio.sleep(5)
print('something')
async def main():
tasks = [user_input(), print_something()]
await asyncio.gather(*tasks)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
Update: Also you can use aioconsole lib, which provides asyncio-based console functions:
from aioconsole import ainput
async def user_input():
while True:
content = await ainput(">")
print(content)
The short answer is async cannot do what you are wanting. While you did declare your functions as async the python function input is not async, it is a blocking function. So it will block the event loop and nothing else will run.
I answered a question awhile back that kinda explains how async works in python. I'll link it. But in order to do what you want you need to use threads not async.
https://stackoverflow.com/a/63237798/9270488
If you want to use threads with async then look into ThreadExecutor

How to throw a custom exception into a running task

I'm trying to figure out if it's possible throw a custom exception into a running asyncio task, similarly to what is achieved by Task.cancel(self) which schedules a CancelledError to be raised in the underlying coroutine.
I came across Task.get_coro().throw(exc), but calling it seems like opening a big can of worms as we may leave the task in a bad state. Especially considering all the machinery that happens when a task is throwing CancelledError into its coroutine.
Consider the following example:
import asyncio
class Reset(Exception):
pass
async def infinite():
while True:
try:
print('work')
await asyncio.sleep(1)
print('more work')
except Reset:
print('reset')
continue
except asyncio.CancelledError:
print('cancel')
break
async def main():
infinite_task = asyncio.create_task(infinite())
await asyncio.sleep(0) # Allow infinite_task to enter its work loop.
infinite_task.get_coro().throw(Reset())
await infinite_task
asyncio.run(main())
## OUTPUT ##
# "work"
# "reset"
# "work"
# hangs forever ... bad :(
Is what I try to do even feasible? It feels as if I shouldn't be manipulating the underlying coroutine like this. Any workaround?
There's no way to throw a custom exception into a running task. You shouldn't mess with .throw - it's a detail of implementation and altering it will probably break something.
If you want to pass information (about reset) into the task, do it trough an argument. Here's how it can be implemented:
import asyncio
from contextlib import suppress
async def infinite(need_reset):
try:
while True:
inner_task = asyncio.create_task(inner_job())
await asyncio.wait(
[
need_reset.wait(),
inner_task
],
return_when=asyncio.FIRST_COMPLETED
)
if need_reset.is_set():
print('reset')
await cancel(inner_task)
need_reset.clear()
except asyncio.CancelledError:
print('cancel')
raise # you should never suppress, see:
# https://stackoverflow.com/a/33578893/1113207
async def inner_job():
print('work')
await asyncio.sleep(1)
print('more work')
async def cancel(task):
# more info: https://stackoverflow.com/a/43810272/1113207
task.cancel()
with suppress(asyncio.CancelledError):
await task
async def main():
need_reset = asyncio.Event()
infinite_task = asyncio.create_task(infinite(need_reset))
await asyncio.sleep(1.5)
need_reset.set()
await asyncio.sleep(1.5)
await cancel(infinite_task)
asyncio.run(main())
Output:
work
more work
work
reset
work
more work
work
cancel

Categories

Resources