Search code examples
pythonpython-3.xasynchronoustornado

Python AsyncHttpClient inside tornado RequestHandler throws exception


I'm going to call an endpoint by tornado AsyncHttpClient in RequestHandler, but it throws runtime exception This event loop is already running

class RegistrationHandler(tornado.web.RequestHandler):

  def post(self, *args, **kwargs):
      call_async_register("some params")


def call_async_register(parameters):
    def call():
        http_client = AsyncHTTPClient()
        future = Future()
        http_request = HTTPRequest(url, request_type.name, headers={'X-Peering': '1'}, body=body)

        def handle_future(f: Future):
            future.set_result(f.result())

        fetched_future = http_client.fetch(http_request)

        fetched_future.add_done_callback(handle_future)

        return future
    try:
        instance = io_loop.IOLoop.current()
        response = instance.run_sync(call)
        return response.body.decode()
    except Exception as err:
        self.logger.exception("Account Request Failed: {}".format(err))
        return None

Solution

  • Here's the problem:

    instance = io_loop.IOLoop.current()
    response = instance.run_sync(call)
    

    run_sync itself tries to start the ioloop. But as apparent from your code, instance is already running. So you get the error.

    If you want to send the value returned by call() method back to the user, convert your methods to coroutines (use async/await syntax).

    Example:

    class RegistrationHandler(tornado.web.RequestHandler):
    
        async def post(self, *args, **kwargs):
            response = await call_async_register("some params")
    
            self.write(response)
    
    
    async def call_async_register(parameters):
        http_client = AsyncHTTPClient()
        http_request = HTTPRequest(url, request_type.name, headers={'X-Peering': '1'}, body=body)
    
        try:
            response = await http_client.fetch(http_request)
            return response.body.decode()
        except Exception as err:
            self.logger.exception("Account Request Failed: {}".format(err))
            return None