I am stumped by a problem I am having with memcache on App Engine. It
mostly looks like a memcache issue but I am seeing something funny when I
step through the code.
I am getting an excpetion (TypeError: 'NoneType' object is not callable)
when writing an instance to memcache. The instance comes from a simple
class that should be pickleable, containing only strings, booleans, an int,
and a datetime.
The reason I am bringing this to the web2py board is that when I traced
down the memcache call chain to a call to pickler.dump() and attempted to
step into it, control jumped to code for web2py's _Web2pyImporter object in
gluon/custom_import.py. Here is the relevant section of my pdb session.
>
/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py(403)_do_pickle()
-> pickler.dump(value)
(Pdb) s
--Call--
> /Users/davidp/dev/python/rage/gluon/custom_import.py(253)__call__()
-> def __call__(self, name, globals=None, locals=None,
I stepped through the _Web2pyImporter code, and eventually, when executing
the following statement, (line 292-3), control jumped into gluaon/dal.py
where it executed the __getattr__() method of the Reference class.
return super(_Web2pyImporter, self).__call__(name, globals, locals,
fromlist, level)
The __getattr__ returns None (it was looking up something called
'__getstate__') which causes something to throw an exception. I am, by this
time, just scratching my head. Why is appengine's memcache code invoking
gluon code? Why is class Reference involved? The object that memcache is
pickling contains no DAL references. Why is "custom importing" needed at
all?
The 'name' parameter to the Web2pyImporter function __call__ has a value of
datetime. There is a datetime field in the object I'm trying to write to
memcache, but why does the code jump to custom_import.py when the call to
pickler.dump() is called?
Here is the memcache code that was being executed at the time:
def _do_pickle(self, value):
"""Pickles a provided value."""
pickle_data = cStringIO.StringIO()
pickler = self._pickler_factory(pickle_data,
protocol=self._pickle_protocol)
if self._persistent_id is not None:
pickler.persistent_id = self._persistent_id
pickler.dump(value) # line 403
return pickle_data.getvalue()
Here is how the pdb showed the control flow as I stepped through
_do_pickle().
(Pdb) n
>
/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py(403)_do_pickle()
-> pickler.dump(value)
(Pdb) s
--Call--
>
/Users/davidp/dev/python/ragearchives/gluon/custom_import.py(253)__call__()
-> def __call__(self, name, globals=None, locals=None,
The stack trace from the exception is copied below. Why doesn't it show the
call into pickler.dump or custom_import? I am most confused. All I want to
do is to store my object in memcache and my client is getting impatient.
Is this a web2py issue? App Engine's memcache? Pickle?
Any help would be appreciated.
File "applications/rage/modules/player.py", line 51, in __init__
memcache.set (key, self, Player.MEMCACHE_EXPIRATION)
File
"/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py",
line 778, in set
namespace=namespace)
File
"/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py",
line 883, in _set_with_policy\n time, \'\', namespace)
File
"/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py",
line 962, in _set_multi_async_with_policy
stored_value, flags = _validate_encode_value(value, self._do_pickle)
File
"/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py",
line 227, in _validate_encode_value
stored_value = do_pickle(value)
File
"/Users/davidp/dev/python/google/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/memcache/__init__.py",
line 403, in _do_pickle
pickler.dump(value)
TypeError: 'NoneType' object is not callable