How to call async function within non async function in python? - python

I know about await and async must be pair. But. here is my problem:
I hava a function like
def url_open(req):
#fetch url content synchronize
and now, I want to change to another version like:
def url_open(req):
await self.websocket.send(req)
content = await self.websocket.recv()
the problem is url_open function was exist in many place in the whole project, and it 'is in the bottom of the call tree. it's not possible to make all the caller to be async function.
So, how can I make this work? get some result from another async functions like in python websocket module?
======= updated ======
After I tried as #Viliam Popovec provided
I got this warning
RuntimeWarning: coroutine 'hello' was never awaited
and error
This event loop is already running
The websocket was run like this in my app
async def main():
async with websockets.serve(echo, '0.0.0.0', 8001):
await asyncio.Future()
asyncio.run(main())
It't seems that the new event loop has conflicts with the existing websocket runloop

If you can't modify all instances of url_open function to be ansynchronous, then you could use loops to call some async function (e.g. hello() in code sample below).
async def hello():
await asyncio.sleep(1)
print("Hello World!")
def url_open():
loop = asyncio.get_event_loop()
loop.run_until_complete(hello())

Related

asyncio.run() cannot be called from a running event loop in FastAPI

In an API developed using FastAPI framework, I am using asyncio to make calls to solr collections, but when I am calling asyncio.run(man(a,b)) from a python file query.py then I am getting asyncio.run() cannot be called from a running event loop.
in controller.py
#router.api_route()
async def api_call(a,b):
#calling a function
resp = query(a,b)
return resp
in query.py
def query(a,b):
result = asyncio.run(man(a,b))
return result
in database_call.py
async def man(a,b)
async with aiohttp.ClientSession() as session:
url = ''
async with session.get(pokemon_url) as resp:
result = await resp.json()
return result
when I am calling asyncio.run(man(a,b)) from query then I am getting asyncio.run() cannot be called from a running event loop. Kindly help me resolve the issue.
I tried:
in query.py
def query(a,b):
loop = asyncio.get_event_loop
result = loop.create_task(man(a,b))
return result
then I am getting <coroutine object main at 0x0394999ejt>
The docs say that you should have only one call to asyncio.run in a program. "Should" doesn't mean the same thing as "must", so it's not a requirement. But it's a good guideline.
Solution 1: get rid of query entirely, and just await the coroutine man() directly.
#router.api_route()
async def api_call(a,b):
return await man(a, b)
Solution 2: declare query to be an async def function, and await it:
#router.api_route()
async def api_call(a,b):
#calling a function
return await query(a,b)
async def query(a,b):
return await man(a,b)
Solution 3: Do not declare query to be async def, but have it return an awaitable object. This is similar to what you tried in your last listing, but you need to await the result.
#router.api_route()
async def api_call(a,b):
#calling a function
return await query(a,b)
def query(a, b):
return asyncio.create_task(man(a, b))
Solution 4: run query in another thread using the asyncio.to_thread function.
Solution 5: run query in a ThreadPool or ProcessPool.
The whole idea of asyncio (or any other form of parallel processing) is to allow the use of a task or a coroutine that does not finish immediately. It runs at a later time, and its result is not available until enough time has passed. Whether you use threading, multiprocessing or asyncio the situation is the same. If you need the answer from the coroutine/task/function in order for your program to continue, you need to wait for it somehow.

How to intergrate telethon script into my event loop?

I have two bots, one is using pydle for IRC, like:
async def start_ircbot ():
try:
client = MyOwnBot(NICK,
realname=REALNAME,
sasl_username=SASL_USERNAME,
sasl_password=SASL_PASSWORD,
sasl_identity=SASL_IDENTITY,)
loop = asyncio.get_event_loop()
asyncio.ensure_future(client.connect(HOST, PORT, tls=True, tls_verify=False), loop=loop)
loop.run_forever()
loop.close()
except Exception as e:
print (e)
and another is using telethon for Telegram:
#client.on(events.NewMessage)
async def my_event_handler(event):
...
async def start_client ():
print ("Telegram monitor started...")
await client.start()
await client.run_until_disconnected()
Both of them work without problem separately.
Now, I want to integrate both of them, I tried to launch both of them in my main function like this,
import Notifier
...
async def main():
await asyncio.gather (Notifier.start_client (), start_ircbot ())
asyncio.run(main())
It starts without issue but my_event_handler seems never to get new messages. If I swap the order of functions:
await asyncio.gather (start_ircbot (), Notifier.start_client ())
The script will be stuck at launching, I suspect it has to be something within events loops and tried some different methods but without luck, could anyone shed light on this for me?
Newer Python versions are removing the loop parameter from most methods, so you should try to avoid using it. As long as you don't use asyncio.run (which creates a new loop) or you don't create a new loop yourself, both libraries should be using the default loop from the main thread (I can't speak for pydle, but Telethon does this).
As long as the asyncio event loop is running, Telethon should have no trouble receiving updates. You can use client.loop to make sure it's using the same loop:
tlclient = TelegramClient(...)
irclient = MyOwnBot(...)
#tlclient.on(events.NewMessage)
async def my_event_handler(event):
...
async def main():
await tlclient.start()
await irclient.connect(HOST, PORT, tls=True, tls_verify=False), loop=tlclient.tlclient)
await tlclient.run_until_disconnected() # similar to loop.run_forever() but stops when the client disconnects
client.loop.run_until_complete(main())

SyntaxError: 'await' outside function | discord.py [duplicate]

This question already has an answer here:
SyntaxError (Python): 'await' outside async function
(1 answer)
Closed 1 year ago.
I know - there are a lot of questions with this title, but most are for Dark Sky or are different to my problem.
class Counter(discord.ui.View):
#discord.ui.button(label='0', style=discord.ButtonStyle.red)
async def counter(self, button: discord.ui.Button, interaction:
discord.Interaction):
number = int(button.label)
button.label = str(number + 1)
if number + 1 >= 5:
button.style = discord.ButtonStyle.green
await interaction.message.edit(view=self)
view = Counter()
await ctx.send('Press to increment', view=view)
Error message:
File "main.py", line 27
await ctx.send('Press to increment', view=view)
^
SyntaxError: 'await' outside function
I'm very new to python and am not sure why this is at all. Any help would be greatly appreciated!
you need to put the
view = Counter()
await ctx.send('Press to increment', view=view)
code inside a asynchron function, because the await keyword can only be used inside of asynchron functions.
example functions
async def my_function(): # this is an async function
...
def my_other_function(): # this is a normal function
...
Async functions have to be called with the await keyword, or else you'll get an exception
await my_function() # calling an async function
my_other_function() # calling a normal function
as you can see in this example, in the async function, you can use await
async def something():
print("hello async world")
async def my_function():
await something()
but if you try to await an async function inside a normal function
asnyc def something():
print("hello async world")
def my_other_function():
await something()
You will get the SyntaxError: 'await' outside function error
The best way to do this together with a discord bot is by adding the code to a listener. For example with the on_message event which will be fired whenever a message is incoming
Example for on_message event
import discord
client = discord.Client()
#client.event
async def on_message(message): # The event
if message.content == "your_text" # Insert a text here that is needed to use the "command"
# your code will be executed whenever the bot receives a message with the content "your_text"
view = Counter()
await message.channel.send('Press to increment', view=view)
client.run("your unique token here")
I would recommend to take a look at https://discordpy.readthedocs.io/en/stable/quickstart.html before starting
According to the ctx, your code is made for the client's #commands.command function.
(this should fix your code)
from discord.ext import commands
bot = commands.Bot(command_prefix="!")
class Counter(discord.ui.View):
#discord.ui.button(label='0', style=discord.ButtonStyle.red)
async def counter(self, button: discord.ui.Button, interaction:
discord.Interaction):
number = int(button.label)
button.label = str(number + 1)
if number + 1 >= 5:
button.style = discord.ButtonStyle.green
await interaction.message.edit(view=self)
#bot.command("test")
async def command(ctx):
view = Counter()
await ctx.send('Press to increment', view=view)
bot.run("your unique token here")
In order to use your code, you need to send a message in a channel where your bot can read messages with the content !test to invoke the command
The typical structure of your code will be (using sample code from the docs)
#bot.command()
async def foo(ctx, arg):
await ctx.send(arg)
That's also where the ctx you're using comes from--if you removed the await you'd get an error that ctx wasn't defined. Your code will specify functions that correspond to commands or other events on discord, and discord.py will handle calling the functions you specify when users perform those actions.
You can only use the await keyword in async functions. The idea behind these functions is that if your code has to do something that takes a long time, but mostly just involves waiting, python can go do other stuff while it waits. For discord.py, since a lot of your operations involve waiting for slow network operations like sending messages, that's very important.
here's one intro to the ideas behind async/await, I haven't read it too much, so if it's bad or anybody has better suggestions drop a link in the comments.

Async pymongo error "attached to a different loop"

I am getting this Runtime error "attached to a different loop"
Edit: I am already using the async motor driver
dBase.py
used for interacting with database this has a function
async def getChatids():
data = list()
async for document in Collection.find({}, {'chatid':1, '_id':0}):
data.append(document['chatid'])
return data
The above function works well while testing in dBase.py using this code below
async def main():
ids = await getChatids()
print(ids)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
While running it in main.py or bot.py (telegram bot using aiogram)
main.py
main running code
async def main():
await asyncio.gather(
bot.dp.start_polling(),
scan() #function to scan api
)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
The scan function calls this fuction below
async def parseData(data):
if(some conditon related to data):
await bot.broadcast("hello broacasting")
broadcast function
async def broadcast(data):
ids = await getChatids()
for id in ids:
await bot.send_message(id,data)
After trying a lot I am unable to get whats the problem is. Any help will be appreciated.
Apparently this line of code below
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
was causing the problem.

Passing async function as argument

I am trying to use use grequests to make a single HTTP call asynchronously.
All goes find until I try to call a function (handle_cars) to handle the response.
The problem is that the function is an async function and it I don't know how to await it while passing.
Is this even possible?
I need the function to be async because there is another async function I need to call from it. Another solution would be to be able to call and await the other async function (send_cars) from inside the sync function.
async def handle_cars(res):
print("GOT IT")
await send_cars()
async def get_cars():
req = grequests.get(URL, hooks=dict(response=handle_cars))
job = grequests.send(req, grequests.Pool(1))
How do I set the response argument to await the function? Or how do I await send_cars if I make handle_cars synchronous?
Thanks
According to the OP's comments, he wishes to create a request in the background, and call a callback when it finishes.
The way to do so can be implemented using asyncio.create_task() and using task.add_done_callback().
A simpler way however, will work by creating a new coroutine and running it in the background.
Demonstrated using aiohttp, it will work in the following way:
async def main_loop():
task = asyncio.create_task(handle_cars(URL))
while True:
...
async def handle_cars(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
await send_cars()

Categories