Commit 637c2bd0 authored by Jim Fulton's avatar Jim Fulton

Non-ghostifiable (persistent classes) objects load their state right

away -- even before they are placed in the cache.  This causes a
problem if the object's state has a (direct or indirect) reference to
it. Added a pre-cache that allows the connection to return objects
alreadey being loaded when necessary.
parent d222028a
...@@ -105,6 +105,12 @@ class Connection(ExportImport, object): ...@@ -105,6 +105,12 @@ class Connection(ExportImport, object):
# recently used. Its API is roughly that of a dict, with # recently used. Its API is roughly that of a dict, with
# additional gc-related and invalidation-related methods. # additional gc-related and invalidation-related methods.
self._cache = PickleCache(self, cache_size) self._cache = PickleCache(self, cache_size)
# The pre-cache is used by get to avoid infinite loops when
# objects immediately load their state whern they get their
# persistent data set.
self._pre_cache = {}
if version: if version:
# Caches for versions end up empty if the version # Caches for versions end up empty if the version
# is not used for a while. Non-version caches # is not used for a while. Non-version caches
...@@ -224,6 +230,9 @@ class Connection(ExportImport, object): ...@@ -224,6 +230,9 @@ class Connection(ExportImport, object):
if obj is not None: if obj is not None:
return obj return obj
obj = self._added.get(oid, None) obj = self._added.get(oid, None)
if obj is not None:
return obj
obj = self._pre_cache.get(oid, None)
if obj is not None: if obj is not None:
return obj return obj
...@@ -233,11 +242,14 @@ class Connection(ExportImport, object): ...@@ -233,11 +242,14 @@ class Connection(ExportImport, object):
p, serial = self._storage.load(oid, self._version) p, serial = self._storage.load(oid, self._version)
obj = self._reader.getGhost(p) obj = self._reader.getGhost(p)
# Avoid infiniate loop if obj tries to load its state before
# it is added to the cache and it's state refers to it.
self._pre_cache[oid] = obj
obj._p_oid = oid obj._p_oid = oid
obj._p_jar = self obj._p_jar = self
obj._p_changed = None obj._p_changed = None
obj._p_serial = serial obj._p_serial = serial
self._pre_cache.pop(oid)
self._cache[oid] = obj self._cache[oid] = obj
return obj return obj
......
...@@ -21,7 +21,28 @@ import unittest ...@@ -21,7 +21,28 @@ import unittest
import ZODB.tests.util import ZODB.tests.util
import transaction import transaction
from zope.testing import doctest from zope.testing import doctest
import ZODB.persistentclass
def class_with_circular_ref_to_self():
"""
It should be possible for a class to reger to itself.
>>> class C:
... __metaclass__ = ZODB.persistentclass.PersistentMetaClass
>>> C.me = C
>>> db = ZODB.tests.util.DB()
>>> conn = db.open()
>>> conn.root()['C'] = C
>>> transaction.commit()
>>> conn2 = db.open()
>>> C2 = conn2.root()['C']
>>> c = C2()
>>> c.__class__.__name__
'C'
"""
# XXX need to update files to get newer testing package # XXX need to update files to get newer testing package
class FakeModule: class FakeModule:
...@@ -44,6 +65,7 @@ def test_suite(): ...@@ -44,6 +65,7 @@ def test_suite():
return unittest.TestSuite(( return unittest.TestSuite((
doctest.DocFileSuite("../persistentclass.txt", doctest.DocFileSuite("../persistentclass.txt",
setUp=setUp, tearDown=tearDown), setUp=setUp, tearDown=tearDown),
doctest.DocTestSuite(setUp=setUp, tearDown=tearDown),
)) ))
if __name__ == '__main__': if __name__ == '__main__':
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment