Expected Behavior
memcache.get() returns an object from memcache, even if it was added to memcache using the python27 runtime.
Actual Behavior
Traceback (most recent call last):
... <truncated> ...
File "/srv/services/secrets_svc.py", line 59, in GetSecrets
secrets = memcache.get(GLOBAL_KEY)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/memcache/__init__.py", line 583, in get
results = rpc.get_result()
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/apiproxy_stub_map.py", line 648, in get_result
return self.__get_result_hook(self)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/memcache/__init__.py", line 652, in __get_hook
value = _decode_value(returned_item.value,
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/memcache/__init__.py", line 289, in _decode_value
return do_unpickle(value)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/memcache/__init__.py", line 425, in _do_unpickle
return unpickler.load()
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 55: ordinal not in range(128)
Steps to Reproduce the Problem
- Call
memcache.set() on an ndb.Model object in python27.
- Call
memcache.get() to get the object in python39.
This reproduces even with MEMCACHE_USE_CROSS_COMPATIBLE_PROTOCOL set in app.yaml. This sets the pickling protocol to 2, which only affects pickling and not unpickling, since the protocol is autodetected upon unpickling.
env_variables:
MEMCACHE_USE_CROSS_COMPATIBLE_PROTOCOL: "2"
Workaround
There is a workaround by setting the encoding to 'bytes' in the memcache unpickler. It must be 'bytes' instead of 'latin1' because the ndb.Model deserializer expects a bytes object.
import functools
import six
from google.appengine.api import memcache
unpickler = functools.partial(six.moves.cPickle.Unpickler, encoding='bytes')
memcache.setup_client(memcache.Client(unpickler=unpickler))
The Python docs and bug tracker indicate that the encoding argument of pickle.Unpickler() is used to help with the awkwardness in differences in str between Python 2 and 3.
https://docs.python.org/3/library/pickle.html#pickle.Unpickler
https://bugs.python.org/issue22005
https://stackoverflow.com/questions/28218466/unpickling-a-python-2-object-with-python-3/28218598#28218598
Specifications
- Version: appengine-python-standard==0.3.1 and appengine-python-standard==1.0.0
- Platform: python39
Expected Behavior
memcache.get()returns an object from memcache, even if it was added to memcache using thepython27runtime.Actual Behavior
Steps to Reproduce the Problem
memcache.set()on anndb.Modelobject in python27.memcache.get()to get the object in python39.This reproduces even with
MEMCACHE_USE_CROSS_COMPATIBLE_PROTOCOLset inapp.yaml. This sets the pickling protocol to 2, which only affects pickling and not unpickling, since the protocol is autodetected upon unpickling.Workaround
There is a workaround by setting the encoding to
'bytes'in the memcache unpickler. It must be'bytes'instead of'latin1'because thendb.Modeldeserializer expects abytesobject.The Python docs and bug tracker indicate that the
encodingargument ofpickle.Unpickler()is used to help with the awkwardness in differences instrbetween Python 2 and 3.https://docs.python.org/3/library/pickle.html#pickle.Unpickler
https://bugs.python.org/issue22005
https://stackoverflow.com/questions/28218466/unpickling-a-python-2-object-with-python-3/28218598#28218598
Specifications