-1

I'm currently working on a small Telegram bot library that uses PTB under the hood.

One of the syntactic sugar features is this decorator function I use, to ensure code locality. Unfortunately, the inner wrapper is never called (in my current library version, I have an additional wrapper called @aexec I place below @async_command, but that's not ideal)

def async_command(name: str):
    def decorator(func):
        updater = PBot.updater
        updater.dispatcher.add_handler(CommandHandler(name, func, run_async=True))
        def wrapper(update: Update, context: CallbackContext):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            loop.run_until_complete(func(update, context))
            loop.close()
        return wrapper
    return decorator

@async_command(name="start")
async def start_command(update: Update, context: CallbackContext) -> None:
    update.message.reply_text(text="Hello World")

"def wrapper" inside "async_command" is never called, so the function is never executed. Any idea how to achieve this without needing an additional decorator to start a new asyncio event loop?

Note: PBot is just a simple class that contains one static "updater" that can be re-used anywhere in the code (PTB uses "updater" instances, which is not ideal for my use cases)

EDIT: I notice that the issue of the inner wrapper not being called only happens on async function. Is this a case of differing calling conventions?

user237251
  • 211
  • 1
  • 10

1 Answers1

0

I actually figured it out myself.

I took the existing @aexec and chained the two functions internally, creating a new decorator.

def aexec(func):
    def wrapper(update: Update, context: CallbackContext):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(func(update, context))
        loop.close()
    return wrapper

def _async_command(name: str):
    def wrapper(func):
        updater = PBot.updater
        updater.dispatcher.add_handler(CommandHandler(name, func, run_async=True))
    return wrapper

def async_command(name: str):
    run_first = _async_command(name)

    def wrapper(func):
        return run_first(aexec(func))
    return wrapper

Now I can use @async_command(name="name_of_command") and it will first call _async_command, then aexec

user237251
  • 211
  • 1
  • 10