0

I'm converting a Google App Engine Python 2 project to Python 3

My understanding from reading the documentation I understand the preferred path is to run the main python program directly, skipping dev_appserver.py as was done in the past.

https://cloud.google.com/appengine/docs/standard/python3/tools/local-devserver-command

python3 main.py

--- main.py -- 
# coding: utf-8

import flask
from flask import request, redirect, url_for
from flask_basicauth import BasicAuth

#from urllib# import urlparse, urlunparse

import config


--- config.py

import model
...
33: CONFIG_DB = model.Config.get_master_db()


--- model/config.py
from __future__ import absolute_import

from google.cloud import ndb
#from oauth2client.contrib.appengine import CredentialsNDBProperty

from api import fields
import config
import model
import util
...

class Config(model.Base, model.ConfigAuth):
...
@classmethod
def get_master_db(cls):
57:  return cls.get_or_insert('master')

When running the following trace is prod

Traceback (most recent call last):
  File "main.py", line 9, in <module>
    import config
  File "/home/ffej/cloudpayback/main/config.py", line 33, in <module>
    CONFIG_DB = model.Config.get_master_db()
  File "/home/ffej/cloudpayback/main/model/config.py", line 57, in get_master_db
    return cls.get_or_insert('master')
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/_options.py", line 89, in wrapper
    return wrapped(*pass_args, **kwargs)
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/utils.py", line 146, in positional_wrapper
    return wrapped(*args, **kwds)
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/model.py", line 5698, in _get_or_insert
    return cls._get_or_insert_async(
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/_options.py", line 89, in wrapper
    return wrapped(*pass_args, **kwargs)
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/utils.py", line 146, in positional_wrapper
    return wrapped(*args, **kwds)
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/model.py", line 5811, in _get_or_insert_async
    key = key_module.Key(cls._get_kind(), name, parent=parent, **key_args)
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/key.py", line 290, in __new__
    context = context_module.get_context()
  File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/context.py", line 96, in get_context
    raise exceptions.ContextError()
google.cloud.ndb.exceptions.ContextError: No current context. NDB calls must be made in context established by google.cloud.ndb.Client.context.

Is there additional logic that should be included after migration to start/initialize the datastore?

Thanks, Jeff

ffejrekaburb
  • 656
  • 1
  • 10
  • 35

1 Answers1

3

The API for NDB library in Python3 has changed significantly. For developing on localhost you have to:

  • run DataStore Emulator, since you're not running dev_appserver anymore:

    $ gcloud beta emulators datastore start

  • if you use the new NDB Library, then each NDB operation needs to be wrapped in a context manager:

with ndb_client.context(): # <- you need this line
    cls.get_or_insert('master')

edit: instead of wrapping each NDB call with a context manager, you can use a middleware which will wrap the whole request cycle into the NDB context:

class NDBMiddleware:
    def __init__(self, app):
        self.app = app
        self.client = ndb_client

    def __call__(self, environ, start_response):
        with self.client.context():
            return self.app(environ, start_response)

app = Flask(__name__)
app.wsgi_app = NDBMiddleware(app.wsgi_app)
yedpodtrzitko
  • 9,035
  • 2
  • 40
  • 42
  • This code is not working, the working version is here: https://github.com/googleapis/python-ndb/issues/146#issuecomment-516024120 – Suat Atan PhD Dec 04 '20 at 18:55
  • @SuatAtanPhD the middleware code is identical with the github code you linked & it works for me, so can you please be more specific what exactly is not working? – yedpodtrzitko Dec 05 '20 at 06:59