Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RuntimeWarning: coroutine 'qr.<locals>.confirm' was never awaited

I'm writing a webapp in Flask. Within one of my routes, I have a function that listens to an API and waits for a payment to be registered. The function is called confirm(). I pass it in the render_template as confirm=confirm and I call it on the page using Jinja2: {{ confirm(cost) }}

I've realised that the function needs to be called asynchronously because otherwise the page doesn't load until the payment is made. However, I'm getting the titular error that the function needs to be awaited. Having read around, I've tried changing the route to async def qr() but Flask won't load it so I'm not sure how await should be used in this case.

async def confirm(cost):
        json = { "action": "account_history", "account": app.config['NANO'], "count": 5, "raw": False, "reverse": False }
        now = datetime.now()
        delta = timedelta(seconds=60)
        while datetime.now() < now+delta:
            test = requests.post("https://nanoverse.io/api/node",json=json).json()
            for item in test["history"]:
                if item["amount"] == cost:
                    flash("Payment Received!")
                    break
            else:
                continue
            break
like image 605
Andrew Avatar asked Oct 16 '22 01:10

Andrew


1 Answers

To be able to use Awaitable objects within jinja2 templates, the environment must be created with the enable_async option. Ref: https://jinja.palletsprojects.com/en/2.11.x/api/#async-support

To do this from flask you need to set it via jinja_options before running the app. Like this:

from flask import Flask

app = Flask(__name__)
app.jinja_options['enable_async'] = True

Now there's only one issue remaining. Based on the AsyncSupport documentation of jinja2:

asyncio.get_event_loop() must return an event loop. Since flask creates a new Thread for each request there will be no evet_loop for jinja. So something like this must be done to get it working:

@app.route('/')
def example():
    # ...
    asyncio.set_event_loop(asyncio.new_event_loop())
    
    return render_template('index.html', confirm=confirm)

Be cautious with how you'd provide the event loop. I don't think this a production-ready approach. This is just a proof of concept to demonstrate how it should work. I think the "best practices to provide event_loop for flask threads" is a whole another question.

like image 52
mehdy Avatar answered Oct 22 '22 10:10

mehdy