1
votes

I'm writing a Discord bot, and I'm having trouble testing an async function. I want to test it by using exec(), but I can't seem to call the function properly.

I've tried exec()-ing the function both with and without an await. I've looked at the API docs, but that hasn't given me much insight into my problem. Using eval(), it returns the coroutine object, but doesn't execute it.

The exec() is done by processing the message with an async function

async def f(message)
    #other stuff
    ...
    ...
    exec(strip2(message.content, "exec"))
    return #exec doesn't return anything, so we return to not send an empty message

The async function looks something like this:

async def move_message(message_id, old_channel, new_channel):
    """
    check the 20 latest messages in old_channel, and if
    one of them matches the id, move it to new_channel
    """
    print("ok")
    async for message in old_channel.history(limit=20):
        #do stuff
        ...
    print("good!")

Without the await, it gives this error: ...\commands.py:1: RuntimeWarning: coroutine 'move_message' was never awaited With the await, it gives me a SyntaxError:

 File "<string>", line 1
    await move_message(message, message.channel, "admin-test-playground")
                     ^
SyntaxError: invalid syntax

I want the function to be executed properly, at least printing something. But neither "ok" nor "good!" prints with what I have now.

1

1 Answers

2
votes

async and await have special syntax. await expr only parses correctly within an async def context. So exec("await function()") will raise a SyntaxError since exec operates by parsing a statement and running it, so it doesn't respect the event loop.

Therefore, you cannot use exec in async code to call async functions. This makes sense once you understand more of how async functions work under the hood, since await changes the behavior of the code more than a function call would.

However, if you switch to using eval, the code you had before should instead return the coroutine object you are calling. Since coroutine objects are awaitable, you can then use this as you were intending with exec.

So you should be able to do something like

def f(message):
    await eval(message.lstrip('exec '))

and have it work correctly.