Search code examples
pythongoogle-app-enginewebapp2

Backwards-incompatibility with webapp2 request handler


Anyone know why I've run into an apparent issue with webapp2 not being fully backwards compatible with Google App Engine's now depreciated webapp framework? When I use a function instead of a RequestHandler class object as request handler I get a TypeError exception because some object isn't callable.

Here's a code excerpt showing the problem:

import wsgiref.handlers
import google.appengine.ext.webapp as webapp

def crypt_browser(request = None, response = None):
    import cardbrowser_crypt
    return cardbrowser_crypt.crypt_browser(request, response)

import cardbrowser_crypt
urlmap = [("/search_crypt", crypt_browser),
          ("/search_crypt2", cardbrowser_crypt.crypt_browser),
]

application = webapp.WSGIApplication(urlmap, debug = True)

def main():
    wsgiref.handlers.CGIHandler().run(application)

The cardbrowser_crypt.crypt_browser object is a class object derived from webapp.RequestHandler with only the get() method defined. The import cardbrowser_crypt statement is expensive, so that's the reason for the indirection through a function. Using the /search_crypt2 URL works fine, while the /search_crypt URL generates an exception:

Traceback (most recent call last):
  File "e:\util\python27\lib\wsgiref\handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "e:\util\google_appengine\lib\webapp2-2.3\webapp2.py", line 1524, in __call__
    return self._internal_error(e)(environ, start_response)
  File "e:\util\google_appengine\lib\webapp2-2.3\webapp2.py", line 1522, in __call__
    return response(environ, start_response)
TypeError: 'crypt_browser' object is not callable

There's no apparent reason why substituting a function for a class object should generate this exception. The webapp2 documentation says that a callable can be used. The documentation states that webapp2 is fully backwards compatible with webapp and using a function here works find with the original webapp.

(Arguably my app is badly designed and could be refactored in some way to avoid this problem. However since the app is gets very little use and works fine with the old Python 2.5 runtime as it is, I'm reluctant to do any significant work to "fix" something that currently isn't broken.)


Solution

  • Greg's response gave me the clue I needed to track down the problem. The new webapp2 module treats callables given as request handlers differently depending on whether they are functions or classes. The old webapp module treated these callables identically. My function was expected to return an instance of RequestHandler by webapp which then would be used to handle the request, but webapp2 expects the function to actually handle the request.

    And so while this means my code isn't compatible with webapp2, using the lazy evaluation feature Greg sugggested is a simple enough way to port my app to the new App Engine environment. Thanks, Greg.