I'm in the process of understanding coroutine from Tornado so let's keep everything simple and the more code you paste, the better.
What I want is to make my homemade function async.
All examples I could find in the documentation falls under the same "hidden" part: AsyncHTTPClient. I'm not looking to do an HTTP call. So please do NOT give me example with that class. I'm interested to create something from scratch. I've tried all possibilities on Tornado coroutine
For now I've been testing with a bash sleep. Here is the code:
import tornado.web
import tornado.httpserver
import tornado.gen
import tornado.concurrent
import subprocess
import os
@tornado.gen.coroutine
def letswait():
fut = tornado.concurrent.Future()
subprocess.check_output(["sleep", "5"])
fut.set_result(42)
return fut
class TestHandler1(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
value = yield letswait()
self.render("test.html", num=value)
class TestHandler2(tornado.web.RequestHandler):
def get(self):
self.render("test.html", num=66)
class Application(tornado.web.Application):
def __init__(self):
DIRNAME = os.path.dirname(__file__)
STATIC_PATH = os.path.join(DIRNAME, '../static')
TEMPLATE_PATH = os.path.join(DIRNAME, '../template')
sets = {
"template_path":TEMPLATE_PATH,
"static_path":STATIC_PATH,
"debug":True,
}
tornado.web.Application.__init__(self, [
(r"/test1", TestHandler1),
(r"/test2", TestHandler2),
], **sets)
def main():
http_server = tornado.httpserver.HTTPServer(Application())
http_server.listen(8888)
print "Let s start"
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
But if I visit test1 then I need to wait for the call to return before I can access test2. From what I've understood, I need to use gen.sleep(5)
. But that's just an example. Let's say instead of running sleep 5
on bash, I'm running ssh somewhere 'do_something'
which takes some time to run.
I've been told "this function isn't asynchronous". So my question is how do I make a custom function asynchronous?
EDIT: After searching a little, I've seen there is tornado.process https://gist.github.com/FZambia/5756470 to be used here. But my subprocess comes from a 3rd party, so it's not really something I can overwrite. So my question is also, how do I integrate 3rd party libraries with that gen.coroutine system?
SOLUTION: Thanks to the comments below I've got a solution:
import tornado.web
import tornado.httpserver
import tornado.gen
import tornado.concurrent
import subprocess
import os
from concurrent import futures
# Create a threadpool, and this can be shared around different python files
# which will not re-create 10 threadpools when we call it.
# we can a handful of executors for running synchronous tasks
# Create a 10 thread threadpool that we can use to call any synchronous/blocking functions
executor = futures.ThreadPoolExecutor(10)
def letswait():
result_future = tornado.concurrent.Future()
subprocess.check_output(["sleep", "5"])
result_future.set_result(42)
return result_future
class TestHandler1(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
value = yield executor.submit(letswait)
self.render("test.html", num=value)
class TestHandler2(tornado.web.RequestHandler):
def get(self):
self.render("test.html", num=66)
class Application(tornado.web.Application):
def __init__(self):
DIRNAME = os.path.dirname(__file__)
STATIC_PATH = os.path.join(DIRNAME, '../static')
TEMPLATE_PATH = os.path.join(DIRNAME, '../template')
sets = {
"template_path":TEMPLATE_PATH,
"static_path":STATIC_PATH,
"debug":True,
}
tornado.web.Application.__init__(self, [
(r"/test1", TestHandler1),
(r"/test2", TestHandler2),
], **sets)
def main():
http_server = tornado.httpserver.HTTPServer(Application())
http_server.listen(8888)
print "Let s start"
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()