Search code examples
pythontornado

self.async_callback missing from classes that inherit from RequestHandler


So I'm learning the Tornado web framework right now, and following a few examples from a book I managed to get authentication with OAuth2/Google almost working. It redirects to Google to prompt a user to sign in, but after signing in it throws the following error at me:

Traceback (most recent call last):
  File "C:\python27\lib\site-packages\tornado\web.py", line 1288, in _stack_context_handle_exception
    raise_exc_info((type, value, traceback))
  File "C:\python27\lib\site-packages\tornado\web.py", line 1475, in wrapper
    result = method(self, *args, **kwargs)
  File "C:\Users\enricojr\Downloads\Github\LearningTornado\grumble_login.py", line 8, in get
    self.get_authenticated_user(callback=self.async_callback(self._on_auth))
AttributeError: 'LoginHandler' object has no attribute 'async_callback'

Upon checking the Tornado source code on Github, it does appear indeed that the async_callback() method is gone. Any idea on how to handle this? Every example of authentication using Tornado's auth module I've seen thus far calls for it to be used, and since I'm new to the whole 'asynchronous web programming' thing I'm not quite sure at this point how else I can do it.

My code is below, Python 2.7 and Tornado 4.0.2:

############################
# grumble_login.py
############################
from tornado.auth import GoogleMixin
from tornado.web import RequestHandler, asynchronous

class LoginHandler(RequestHandler, GoogleMixin):
    @asynchronous
    def get(self):
        if self.get_argument("openid.mode", None):
            self.get_authenticated_user(callback=self.async_callback(self._on_auth))
            return
        else:
            self.authenticate_redirect()

    def _on_auth(self, user):
        if not user:
            self.clear_all_cookies()
            raise tornado.web.HTTPError(500, "Google auth failed.")
        else:
            # the user id serves as the basis for 
            # all the data we store.
            self.set_secure_cookie('user_id', user['id'])
            self.redirect("/")

class LogoutHandler(RequestHandler):
    def get(self):
        self.clear_all_cookies()
        self.render("logout.html")

############################
# main.py
############################
import os

from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.web import Application
from tornado.options import define, options

from grumble_handlers import *
from grumble_user_handlers import *
from grumble_login import *


# /user/profile/<user> - leads to profile page
# /user/posts - leads to posts made by user

# /post/<id> - leads to a specific post
# /post/add - add post
# /post/edit - edit post
# /post/delete - delete post
# D$oP5lz3D$oP5lz3

TEMPLATE_PATH = os.path.join(os.path.dirname(__file__), "templates")
STATIC_PATH = os.path.join(os.path.dirname(__file__), "static")

if __name__ == "__main__":
    define("port", default=8000, help="run on the given port", type=int)

    settings_dict = {
        'cookie_secret': "GENERATE NEW KEY HERE",
        'template_path': TEMPLATE_PATH,
        'static_path': STATIC_PATH,
        'debug': True,
        'login_url': "/login",
    }

    general_handlers = [
        (r"/", IndexHandler),
        (r"/login", LoginHandler),
        (r"/logout", LogoutHandler)
    ]

    post_handler_list =[
        (r"/post/(\d+)", PostFetchHandler), # leads to a single post page
        (r"/post/add", PostAddHandler),
        (r"/post/edit", PostEditHandler),
        (r"/post/delete", PostDeleteHandler),
    ]

    user_handler_list = [
        (r"/user/(\d+)/posts", UserPostsHandler), # lists out all the posts by a given user
        (r"/user/(\d+)/profile", UserProfileHandler),
    ]

    master_handler_list = general_handlers + post_handler_list + user_handler_list

    app = Application(
        handlers=master_handler_list,
        **settings_dict
    )

    http_server = HTTPServer(app)
    http_server.listen(options.port)

    # GO TEAM GO
    IOLoop.instance().start()

############################
# grumble_handlers.py
############################
...
class IndexHandler(NotImplementedHandler):
    @authenticated
    def get(self):
        pass

Solution

  • The book is, unfortunately, long outdated. async_callback was removed in Tornado 4 since it is no longer required. You can simply do:

    self.get_authenticated_user(callback=self._on_auth)
    

    Once you have that working, I recommend following the coroutine documents and learn how to use "yield":

    user = yield self.get_authenticated_user()