'Discord Python bot - Coroutine never awaited

I'm trying to make a Blind-test bot game in Python, which actually works pretty fine. I managed to get a whole spotify playlist in the queue with that play_next() function.

def play_next(ctx):

    if len(song_queue) >= 1:
        del song_queue[0]
        source = song_queue[0]
        vc.play(FFmpegPCMAudio(source=source.preview_url),
            after=lambda e: play_next(ctx))
        track_played = source

@client.command()
async def start(ctx):
    ...
    ...
    for song in tracks:
        if not song.track.preview_url:
            continue
        source = song.track
        song_queue.append(source)
        if not vc.is_playing():
            vc.play(FFmpegPCMAudio(source=source.preview_url),
                after=lambda e: play_next(ctx))
            track_played = source

As soon I turn the play_next() function to async to output details about the tracks being played, problems start.

async def play_next(ctx):

    await ctx.send("The answer was: {} from {}".format(track_played.name,
        ' and'.join(track_played.artists)))
    if len(song_queue) >= 1:
        del song_queue[0]
        source = song_queue[0]
        await ctx.send('Next song is about to start')
        vc.play(FFmpegPCMAudio(source=source.preview_url),
            after=lambda e: play_next(ctx))
        track_played = source

Running the previous code throws me this error:

play_next() was never awaited

How can I solve this?



Solution 1:[1]

(Note: this answer was originally added in Revision 3 of the question.)


I finally came across a solution, which I honestly don't know if it's safe or if it is good practice.

I had to leave that play_next() function synchronous, and get the calls to ctx.send() in another async function, as follows:

async def sendMsg(message, ctx):
    await ctx.send(message)

and use asyncio to call the coroutine in the main loop thread. See:

def play_next(ctx):
    ...
    send_fut = asyncio.run_coroutine_threadsafe(sendMsg(message, ctx), loop)
    send_fut.result()
    if len(song_queue) >= 1:
        source = song_queue[0]
        message = ('Next song is about to start')
        send_fut = asyncio.run_coroutine_threadsafe(sendMsg(message, ctx), loop)
        send_fut.result()
        del song_queue[0]
        track_played = source
        vc.play(FFmpegPCMAudio(source=source.preview_url),
                after=lambda x: play_next(ctx))

Having previously initialised loop in the main() with a global variable. Note that I struggled a lot with those two lines as I tried numerous orders and places before finding the right one. Alose note the get_event_loop() instead of new_event_loop().

def main():
    global loop

    loop = asyncio.get_event_loop()
    asyncio.set_event_loop(loop)
    def_globals()
    client.run('TOKEN')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1