Search code examples
pythonpython-3.xasynchronoustornado

Different behaviour for Yield and Await in case of RequestHandler write ( Tornado Web Framework )


I am using RequestHandler for making Web Based call using Tornado Web framework. Previously I was using Tornado Version 5.1.1 which supported @gen.coroutine and yield.

I am moving my tornado version to 6.0.2 and recently as they have depreciated decorated coroutines So I am moving my code to native coroutines.

But I noticed different behavior between decorated coroutines and native coroutines.

Example :

  • main.py:
import tornado.ioloop as ioloop
import tornado.web as web
from RequestHandler import MainHandler

def main():
    application = web.Application([
        (r"/", MainHandler),
    ])
    application.listen(8080)

    ioloop.IOLoop.current().start()

if __name__ == '__main__':
    main()
  • MainHandler ( Tornado Version 5.0.2 )
import tornado.web
import tornado.gen

class MainHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.data = "Hello World"

    @gen.coroutine
    def get(self):
        yield self.write(self.data)

    @gen.coroutine
    def post(self):
        yield self.write(self.data)

  • MainHandler ( Version 6.0.2 )
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.data = "Hello World"

    async def get(self):
        self.write(self.data)

    async def post(self):
        self.write(self.data)

In the case of Decorated Coroutines, it was working fine but when I made it async-await and When I added await for self.write() I started getting error

Error :

ERROR:tornado.application:Uncaught exception GET / (::1)
HTTPServerRequest(protocol='http', host='localhost:8080', method='GET', uri='/', version='HTTP/1.1', remote_ip='::1')
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/tornado/web.py", line 1699, in _execute
    result = await result
  File "/Users/sharvin/Desktop/Personal_Projects/All_Tornado_Projects/1. Tornado_Basics/MainHandler.py", line 8, in get
    await self.write(self.data)
TypeError: object NoneType can't be used in 'await' expression
ERROR:tornado.access:500 GET / (::1) 3.45ms

I am using self.write to write the data on the network.

I checked the Tornado Documentation and according to that RequestHandler's write method doesn't support the Future object.:

RequestHandler.write(chunk: Union[str, bytes, dict]) → None

Question

According to the documentation, it is not necessary to add await for self.write. I am confused will this make self.write asynchronous on the network without adding await?

Same for the WebSocketHandler.close() method.


Solution

  • write() doesn't actually write the data to the network. It only writes the data to the memory buffer. This operation is very fast and doesn't need to be asynchronous.

    To actually send the data to the network, there's another method called flush() which can be awaited/yielded.

    flush() is useful in those cases where you've to return data in chunks or in a loop:

    while True:
        self.write("some data") # writes to buffer
        await self.flush() # writes to network
    

    If you're returning all your data in one go, you don't have to worry about calling flush(). Tornado automatically does that for you when the handler exits.