GoogleCloudPlatform / webapp2

webapp2 is a framework for Google App Engine
https://webapp2.readthedocs.org
Other
141 stars 63 forks source link

Wrong Adapter selected default test environment for multi-inheritance handlers. #74

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
I'm following the suggestions on these pages for setting up unittesting.

https://developers.google.com/appengine/docs/python/tools/localunittesting
https://developers.google.com/appengine/docs/python/tools/handlertesting
http://webapp-improved.appspot.com/guide/app.html#unit-testing

Fairly basic. The problem came in when I had a handler that derived from both 
webapp (indirectly) and webapp2.

Something like this:

class RequestHandler(webapp2.RequestHandler):
  def dispatch(self, *args, **kwargs):
    self.needed_value = 'foo'
    super(RequestHandler, self).dispatch(*args, *kwargs)

class MyHandler(
    blobstore_handlers.BlobstoreUploadHandler,
    webapp2.RequestHandler
):
  def get(self, *args, **kwargs):
    if self.needed_value == 'foo':
      ...

I tracked the problem down to these sections of webapp2.py:
Line 39:
# google.appengine.ext.webapp imports webapp2 in the
# App Engine Python 2.7 runtime.
if os.environ.get('APPENGINE_RUNTIME') != 'python27': # pragma: no cover
    try:
        from google.appengine.ext import webapp as _webapp
    except ImportError: # pragma: no cover
        # Running webapp2 outside of GAE.
        pass

and line 1299 in default_adapter 
            if _webapp and issubclass(handler, _webapp.RequestHandler):
                # Compatible with webapp.RequestHandler.
                adapter = WebappHandlerAdapter
            else:
                # Default, compatible with webapp2.RequestHandler.
                adapter = Webapp2HandlerAdapter

None of the docs say that APPENGINE_RUNTIME needs to be set. So the import 
block imports _webapp. Then later, because my handler inherits from 
blobstore_handlers.BlobstoreUploadHandler which in turn inherits from 
webapp.RequestHandler, my unittests were getting WebappHandlerAdapter, which 
bypassed my dispatch().

My quick fix was to set APPENGINE_RUNTIME == 'python27'. It might be more 
robust if we changed the adapter selector. Looking at the adapter code, the 
main difference seems to be the existence of a dispatch method. Maybe:
if hasattr(handler, 'dispatch'):
  adapter = Webapp2HandlerAdapter
elif _webapp and issubclass(handler, _webapp.RequestHandler):
  adapter = WebappHandlerAdapter

or just change the test
if _webapp and issubclass(handler, _webapp.RequestHandler) and not 
issubclass(handler, RequestHandler):

Original issue reported on code.google.com by jo2y@midnightlinux.com on 12 Feb 2013 at 2:14