Search code examples
python-2.7google-app-enginegoogle-app-engine-python

Google AppEngine dev_appserver raises ImportError despite package being installed in virtualenv and available in Google Cloud SDK


I'm working on a Google AppEngine project that uses the legacy Python 2.7 runtime - my team is migrating away from this, however we're still using it for the time being. Local development has worked fine for me for the last ~6 months, and no one else on my team is affected by the following issue.

I'm getting an ImportError when starting dev_appserver.py, as well as during handling of any requests:

09:36:59 dev_server.1    | ERROR    2021-05-13 13:36:59,470 wsgi.py:269] 
09:36:59 dev_server.1    | Traceback (most recent call last):
09:36:59 dev_server.1    |   File "/Users/me/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 240, in Handle
09:36:59 dev_server.1    |     handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
09:36:59 dev_server.1    |   File "/Users/me/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 311, in _LoadHandler
09:36:59 dev_server.1    |     handler, path, err = LoadObject(self._handler)
09:36:59 dev_server.1    |   File "/Users/me/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 85, in LoadObject
09:36:59 dev_server.1    |     obj = __import__(path[0])
09:36:59 dev_server.1    |   File "/Users/me/Source/myproject/main.py", line 5, in <module>
09:36:59 dev_server.1    |     import webapp2
09:36:59 dev_server.1    |   File "/Users/me/google-cloud-sdk/platform/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 25, in <module>
09:36:59 dev_server.1    |     import webob
09:36:59 dev_server.1    |   File "/private/var/folders/qs/2qn9hbhn3k77wbhgsg77w6tm0000gn/T/pip-target-etkWjd/lib/python/webob/__init__.py", line 1, in <module>
09:36:59 dev_server.1    |   File "/Users/me/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/python/runtime/sandbox.py", line 1168, in load_module
09:36:59 dev_server.1    |     raise ImportError('No module named %s' % fullname)
09:36:59 dev_server.1    | ImportError: No module named webob.datetime_utils

As you can see, the error happens on importing webapp2, which calls import webob. webob is installed both in my virtualenv:

(myenv) me@pegasus site % pip list | grep WebOb
WebOb                         1.8.7

As well as specified in app.yaml:

libraries:
...
- name: webob
  version: "1.2.3"
...

I've ensured that my virtualenv is active at the time I start dev_appserver.py.

The curious thing to me is this line of the traceback:

09:36:59 dev_server.1  |  File "/private/var/folders/qs/2qn9hbhn3k77wbhgsg77w6tm0000gn/T/pip-target-etkWjd/lib/python/webob/__init__.py", line 1, in <module>

I have no idea why code in /private/var/folders is being executed - shouldn't it be calling the code in my virtualenv or the code provided with the Google Cloud SDK?

It appears to be in the SDK - the caller is google-cloud-sdk/platform/google_appengine/lib/webapp2-2.5.2/webapp2.py, however I can find no reason as to why the resolved path for webob is in /private/var/folders as opposed to my virtualenv or, more sensibly, the webob-1.2.3 package also located in google-cloud-sdk/platform/google_appengine/lib/!

In fact, in ~6 years of developing Python full time, I've never seen such a filepath in a traceback - I'd always expect executed code to be in either my own files or site-packages of the associated virtualenv.

Additionally, the file in the traceback doesn't exist (or isn't visible to me, at least): attempting to ls the /private/var/folders/qs/2qn9hbhn3k77wbhgsg77w6tm0000gn/T/pip-target-etkWjd/ directory results in No such file or directory.

What I've tried:

  1. Completely removed & reinstalled both my virtualenv and Google Cloud SDK.
  2. Removed the pip installed instance of webob.
  3. Reinstalled webob with the same version that comes with the SDK (1.2.3).
  4. Installed a new instance of python2.7 with pyenv and used it to remove & re-create my virtualenv.
  5. Removed all extraneous entries from my PATH to see if some entry was causing the import system to resolve webob in an unexpected way.

None of this has worked. Ultimately, I'm still not sure how an import can ever fail if the package is installed in the active virtualenv - I'd expect that the webapp2 module in the Google Cloud SDK would resolve the import via the virtualenv, but I'm definitely not an expert on the import system.


Solution

  • I ended up adding the following code to /Users/me/google-cloud-sdk/platform/google_appengine/lib/webapp2-2.5.2/webapp2.py before the attempted import of webob:

    import imp
    fp, pathname, description = imp.find_module('webob')
    print '--------------------------'
    print fp
    print pathname
    print description
    print '--------------------------'
    

    Which printed out:

    14:03:13 dev_server.1    |--------------------------
    14:03:13 dev_server.1    | None
    14:03:13 dev_server.1    | /Users/me/Source/myproject/lib/webob
    14:03:13 dev_server.1    | ('', '', 5)
    14:03:13 dev_server.1    | --------------------------
    

    This webob directory in lib was unexpected - it's included with the python2 runtime on GAE as well as in the SDK, and thus doesn't need to be vendored in lib. It was also empty, and so I hadn't noticed it because git didn't report any new untracked files.

    I'm not quite sure of the mechanism, but this was causing the import issue - as soon as I removed the empty directory, the issue was resolved.