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

Reply via email to