2
votes

Intent

I am creating a CRUD app for App Engine where the user authenticates via OAuth and the server stores their access and refresh tokens in Datastore. I am doing local testing with dev_appserver and getting stuck between a rock and hard place:

  • The python37 runtime cannot import the ndb module for Datastore and the python27 runtime cannot import the google.auth module.

Problem Statement

Python37

In the python37 runtime, my requirements.txt file is detected and all dependencies are installed properly. However, it appears that this runtime does not currently have native support for the ndb module in order to make calls the Datastore API.

    from google.appengine.ext import ndb
ModuleNotFoundError: No module named 'google.appengine'

One way to solve my problem would be to manually install the ndb package, however I don't believe that it open-source at this point in time.

I will most likely move forward by using google-api-python-client to interact with Datastore. However, I am still interested in solving the problem outlined below in order to better my understanding of the way python modules work in App Engine.

Python27

In the python27 runtime, requirements.txt is ignored. My workaround has been to include the latest releases of all of the dependencies' source code. I do this by populating a directory called dependencies with the source and then add symbolic links to root modules. (This has also been a nice exercise in learning how importing python modules works)

Project Structure

Directory

.
├── api.py
├── app
│   ├── build
│   ├── config
│   ├── dist
│   ├── index.html
│   ├── node_modules
│   ├── package.json
│   ├── package-lock.json
│   ├── src
│   └── static
├── app.yaml
├── dependencies
│   ├── click-7.0
│   ├── flask-1.0.2
│   ├── google-auth-library-python-1.6.3
│   ├── google-auth-library-python-oauthlib-0.3.0
│   ├── itsdangerous-1.1.0
│   ├── jinja-2.10.1
│   └── markupsafe-1.1.1
├── libs
│   ├── click -> ../dependencies/click-7.0/click/
│   ├── flask -> ../dependencies/flask-1.0.2/flask/
│   ├── google -> ../dependencies/google-auth-library-python-1.6.3/google/
│   ├── google_auth_oauthlib -> ../dependencies/google-auth-library-python-oauthlib-0.3.0/google_auth_oauthlib/
│   ├── __init__.py
│   ├── itsdangerous -> ../dependencies/itsdangerous-1.1.0/src/itsdangerous/
│   ├── jinja2 -> ../dependencies/jinja-2.10.1/jinja2/
│   └── markupsafe -> ../dependencies/markupsafe-1.1.1/src/markupsafe/
├── requirements.txt
└── spa.py

Python Code

In my server files, spa.py and api.py, I include libs in the python path as follows:

import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'libs'))

This works for import statements such as

import google_auth_oauthlib.flow  # works

and

from flask import Flask, request, session, redirect  # works

but it fails for

import google.auth  # does not work

with the exception

Traceback (most recent call last):
  File "<HOME>/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 240, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "<HOME>/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 299, in _LoadHandler
    handler, path, err = LoadObject(self._handler)
  File "<HOME>/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 85, in LoadObject
    obj = __import__(path[0])
  File "<HOME>/vue-scheduler/api.py", line 6, in <module>
    import google.auth
  File "<HOME>/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/python/runtime/sandbox.py", line 1149, in load_module
    raise ImportError('No module named %s' % fullname)
ImportError: No module named google.auth

I believe I've set everything up correctly for python to import the auth submodule. The __init__.py files are present and that should let python recurse through the nested module directories. However it isn't able to find auth.

2

2 Answers

0
votes

The ndb ORM library is not available for Python 3. You can either:

  1. Access Cloud Datastore through the Cloud Datastore API. You can use the Google Cloud client libraries to store and retrieve data from Cloud Datastore.
  2. Wait for ndb to be ported to Python 3. This is currently in progress, you can follow along here: https://github.com/Googleapis/python-ndb

Continuing to use the Python 2.7 runtime is not recommended, as Python 2.7 will reach end-of-life on January 1st, 2020.

0
votes

For the 1st generation GAE standard environment (Python 2.7) application handling dependencies is a bit different than for a regular Python application. Try not to get creative as it'll be more difficult to get help if you get in trouble.

For the libraries included in the GAE-provided 3rd party built-in libraries you can just request them in your app.yaml file.

The 3rd party libraries not provided by GAE (or those for which you intentionally want to use your own versions/copies) must be copied/vendored-in into your application.

Choose one method only, attempting to do both for the same library may cause trouble as well.