Carson T
Carson T

Reputation: 1

Run async function from sync function in different thread (Discord.py)

I have 2 files, each running on different threads currently. One for the main file which runs a flask website, one for a discord bot in a seperate thread. I am trying to call a async method on the discord side to send a message to a user once a request is received.

#IN DISCORD FILE (discbot.py)
client = discord.Client()

async def registerSuccess(state, btag):
    ...
    await client.wait_until_ready()
    guilds = client.fetch_guilds()
    async for guild in guilds:
        name = await guild.name
        print(name)
    
#IN FLASK/MAIN FILE (main.py)
@app.route("/oauth", methods=["POST", "GET"])
def oauth():
...
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    #loop.run_until_complete(discbot.registerSuccess(state, btag)) - Did not work
    #asyncio.run(discbot.registerSuccess(state, btag)) - Did not work

# Both of these two above gave me:
#
# "RuntimeError: Task <Task pending coro=#<registerSuccess() running at
# /home/container/arcanabot.py:78> cb=[_run_until_complete_cb() at
# /usr/local/lib/python3.7/asyncio/base_events.py:157]> got Future
# <Future pending> attached to a different loop"


    asyncio.run_coroutine_threadsafe(discbot.registerSuccess(state, btag), loop) - Did not work

#This one never managed to run the function at all

I am not very familiar with async functions and asyncio and have had no success with anything above.

Upvotes: 0

Views: 1102

Answers (1)

Trevor
Trevor

Reputation: 595

Most of the time, it's not necessary to use Threading while coding in an asynchronous fashion, such as discord.py. We can achieve what you want using asyncio.create_task.

What does asyncio.create_task() do?

It submits the coroutine to run "in the background", i.e. concurrently with the current task and all other tasks, switching between them at await points. It returns an awaitable handle called a "task" which you can also use to cancel the execution of the coroutine.

asyncio.run won't work because the Discord Client is started using that method. You aren't able to start another function on the loop if there's already another running.

How to use create_task

#IN DISCORD FILE (discbot.py)
client = discord.Client()

async def registerSuccess(state, btag):
    await client.wait_until_ready()
   
    async for guild in client.fetch_guilds(limit=None):
        print(guild.name)
    
#IN FLASK/MAIN FILE (main.py)
@app.route("/oauth", methods=["POST", "GET"])
def oauth(...) -> None:
    asyncio.create_task(discbot.registerSuccess(state, btag))

Upvotes: 1

Related Questions