zopefoundation/persistent — pull request #6 by jamadden (2014-02-18 20:35:00+00:00)
Title: The pure-python Persistent class raises KeyError during ZODB serialization

While working to make `zope.container` work with PyPy, I noticed that `persistent.persistence.Persistent._p_accessed` raises `KeyError` when ZODB transactions are written, which naturally aborts the transaction::

File "zope.container/eggs/ZODB-4.0.0-py2.7.egg/ZODB/Connection.py", line 660, in _store_objects
p = writer.serialize(obj) # This calls __getstate__ of obj
File "zope.container/eggs/ZODB-4.0.0-py2.7.egg/ZODB/serialize.py", line 423, in serialize
meta = klass, newargs()
File "zope.container/src/zope/container/_proxy.py", line 89, in __getnewargs__
return self._wrapped,
File "zope.container/src/zope/container/_proxy.py", line 143, in __getattribute__
return super(PyProxyBase, self).__getattribute__(name)
File "zope.container/eggs/persistent-4.0.6-py2.7.egg/persistent/persistence.py", line 221, in __getattribute__
_OGA(self, '_p_accessed')()
File "zope.container/eggs/persistent-4.0.6-py2.7.egg/persistent/persistence.py", line 394, in _p_accessed
self.__jar._cache.mru(self.__oid)
File "zope.container/eggs/persistent-4.0.6-py2.7.egg/persistent/picklecache.py", line 113, in mru
value = self.data[oid]
File "pypy/lib-python/2.7/weakref.py", line 63, in __getitem__
o = self.data[key]()
KeyError: ';\x8eh\xdcq\xfd\\\xa7'

As near as I can tell, what's happening is that this object is a newly reachable persistent object, so `ZODB.serialize.ObjectWriter:persistent_id` assigns it a jar and an OID. But that doesn't do anything about putting it in the pickle cache. When the object tries to execute `__getstate__`, invariably accessing attributes that aren't "special" (_p_) it notices that it has a jar and OID and tries to tell the pickle cache that it has been accessed, but because it's not actually in the cache yet, that fails.

This commit catches the error. There may be a better way, or this may be a symptom of a deeper problem, but that seems to work enough for `zope.container` anyway.