[Zodb-checkins] SVN: ZODB/branches/tseaver-python_picklecache-2/src/persistent/ Document 'new_ghost' as part of IPickleCache, and implement it.
Tres Seaver
tseaver at palladion.com
Wed Feb 16 21:32:13 EST 2011
Log message for revision 120401:
Document 'new_ghost' as part of IPickleCache, and implement it.
Add missing coverage.
Changed:
U ZODB/branches/tseaver-python_picklecache-2/src/persistent/interfaces.py
U ZODB/branches/tseaver-python_picklecache-2/src/persistent/picklecache.py
U ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_picklecache.py
-=-
Modified: ZODB/branches/tseaver-python_picklecache-2/src/persistent/interfaces.py
===================================================================
--- ZODB/branches/tseaver-python_picklecache-2/src/persistent/interfaces.py 2011-02-17 02:31:46 UTC (rev 120400)
+++ ZODB/branches/tseaver-python_picklecache-2/src/persistent/interfaces.py 2011-02-17 02:32:13 UTC (rev 120401)
@@ -489,6 +489,21 @@
o XXX?
"""
+ def new_ghost(oid, obj):
+ """ Add the given (ghost) object to the cache.
+
+ Also, set its _p_jar and _p_oid, and ensure it is in the
+ GHOST state.
+
+ If the object doesn't define '_p_oid' / '_p_jar', raise.
+
+ If the object's '_p_oid' is not None, raise.
+
+ If the object's '_p_jar' is not None, raise.
+
+ If 'oid' is already in the cache, raise.
+ """
+
def reify(to_reify):
""" Reify the indicated objects.
@@ -498,7 +513,7 @@
o For each OID, if present in 'data' and in GHOST state:
- o Call '_p_unghostify' on the object.
+ o Call '_p_activate' on the object.
o Add it to the ring.
Modified: ZODB/branches/tseaver-python_picklecache-2/src/persistent/picklecache.py
===================================================================
--- ZODB/branches/tseaver-python_picklecache-2/src/persistent/picklecache.py 2011-02-17 02:31:46 UTC (rev 120400)
+++ ZODB/branches/tseaver-python_picklecache-2/src/persistent/picklecache.py 2011-02-17 02:32:13 UTC (rev 120401)
@@ -11,6 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
+import gc
import weakref
from zope.interface import implements
@@ -60,7 +61,7 @@
def __setitem__(self, oid, value):
""" See IPickleCache.
"""
- if not isinstance(oid, str):
+ if not isinstance(oid, str): # XXX bytes
raise ValueError('OID must be string: %s' % oid)
# XXX
if oid in self.persistent_classes or oid in self.data:
@@ -170,11 +171,36 @@
minimize = full_sweep
- def reify(self, oid):
+ def new_ghost(self, oid, obj):
""" See IPickleCache.
"""
- pass
+ if obj._p_oid is not None:
+ raise ValueError('Object already has oid')
+ if obj._p_jar is not None:
+ raise ValueError('Object already has jar')
+ if oid in self.persistent_classes or oid in self.data:
+ raise KeyError('Duplicate OID: %s' % oid)
+ obj._p_oid = oid
+ obj._p_jar = self.jar
+ if type(obj) is not type:
+ if obj._p_state != GHOST:
+ obj._p_invalidate()
+ self[oid] = obj
+ def reify(self, to_reify):
+ """ See IPickleCache.
+ """
+ if isinstance(to_reify, str): #bytes
+ to_reify = [to_reify]
+ for oid in to_reify:
+ value = self[oid]
+ if value._p_state == GHOST:
+ value._p_activate()
+ self.non_ghost_count += 1
+ mru = self.ring.prev
+ self.ring.prev = node = RingNode(value, self.ring, mru)
+ mru.next = node
+
def invalidate(self, to_invalidate):
""" See IPickleCache.
"""
@@ -185,20 +211,20 @@
self._invalidate(oid)
def debug_info(self):
- """ See IPickleCache.
- """
result = []
for oid, klass in self.persistent_classes.items():
result.append((oid,
- len(gc.getreferents(value)),
- type(value).__name__,
- value._p_state,
+ len(gc.getreferents(klass)),
+ type(klass).__name__,
+ klass._p_state,
))
for oid, value in self.data.items():
result.append((oid,
len(gc.getreferents(value)),
type(value).__name__,
+ value._p_state,
))
+ return result
cache_size = property(lambda self: self.target_size)
cache_drain_resistance = property(lambda self: self.drain_resistance)
@@ -220,8 +246,7 @@
def _invalidate(self, oid):
value = self.data.get(oid)
if value is not None and value._p_state != GHOST:
- # value._p_invalidate() # NOOOO, we'll do it ourselves.
- value._p_ghostify() # TBD
+ value._p_invalidate()
node = self.ring.next
while node is not self.ring:
if node.object is value:
Modified: ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_picklecache.py
===================================================================
--- ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_picklecache.py 2011-02-17 02:31:46 UTC (rev 120400)
+++ ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_picklecache.py 2011-02-17 02:32:13 UTC (rev 120401)
@@ -13,6 +13,8 @@
##############################################################################
import unittest
+_marker = object()
+
class PickleCacheTests(unittest.TestCase):
def _getTargetClass(self):
@@ -24,11 +26,11 @@
jar = DummyConnection()
return self._getTargetClass()(jar, target_size)
- def _makePersist(self, state=None, oid='foo', jar=None):
+ def _makePersist(self, state=None, oid='foo', jar=_marker):
+ from persistent.interfaces import GHOST
if state is None:
- from persistent.interfaces import GHOST
state = GHOST
- if jar is None:
+ if jar is _marker:
jar = DummyConnection()
persist = DummyPersistent()
persist._p_state = state
@@ -272,6 +274,7 @@
self.assertEqual(items[2][0], 'three')
def test_incrgc_simple(self):
+ import gc
from persistent.interfaces import UPTODATE
cache = self._makeOne()
oids = []
@@ -282,6 +285,7 @@
self.assertEqual(cache.cache_non_ghost_count, 100)
cache.incrgc()
+ gc.collect() # banish the ghosts who are no longer in the ring
self.assertEqual(cache.cache_non_ghost_count, 10)
items = cache.lru_items()
@@ -303,14 +307,257 @@
for oid in oids[90:]:
self.failIf(cache.get(oid) is None)
+ def test_incrgc_w_smaller_drain_resistance(self):
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ cache.drain_resistance = 2
+ oids = []
+ for i in range(100):
+ oid = 'oid_%04d' % i
+ oids.append(oid)
+ cache[oid] = self._makePersist(oid=oid, state=UPTODATE)
+ self.assertEqual(cache.cache_non_ghost_count, 100)
+ cache.incrgc()
+
+ self.assertEqual(cache.cache_non_ghost_count, 10)
+
+ def test_incrgc_w_larger_drain_resistance(self):
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ cache.drain_resistance = 2
+ cache.target_size = 90
+ oids = []
+ for i in range(100):
+ oid = 'oid_%04d' % i
+ oids.append(oid)
+ cache[oid] = self._makePersist(oid=oid, state=UPTODATE)
+ self.assertEqual(cache.cache_non_ghost_count, 100)
+
+ cache.incrgc()
+
+ self.assertEqual(cache.cache_non_ghost_count, 49)
+
+ def test_full_sweep(self):
+ import gc
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ oids = []
+ for i in range(100):
+ oid = 'oid_%04d' % i
+ oids.append(oid)
+ cache[oid] = self._makePersist(oid=oid, state=UPTODATE)
+ self.assertEqual(cache.cache_non_ghost_count, 100)
+
+ cache.full_sweep()
+ gc.collect() # banish the ghosts who are no longer in the ring
+
+ self.assertEqual(cache.cache_non_ghost_count, 0)
+ self.failUnless(cache.ring.next is cache.ring)
+
+ for oid in oids:
+ self.failUnless(cache.get(oid) is None)
+
+ def test_minimize(self):
+ import gc
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ oids = []
+ for i in range(100):
+ oid = 'oid_%04d' % i
+ oids.append(oid)
+ cache[oid] = self._makePersist(oid=oid, state=UPTODATE)
+ self.assertEqual(cache.cache_non_ghost_count, 100)
+
+ cache.minimize()
+ gc.collect() # banish the ghosts who are no longer in the ring
+
+ self.assertEqual(cache.cache_non_ghost_count, 0)
+
+ for oid in oids:
+ self.failUnless(cache.get(oid) is None)
+
+ def test_new_ghost_non_persistent_object(self):
+ cache = self._makeOne()
+ self.assertRaises(AttributeError, cache.new_ghost, '123', object())
+
+ def test_new_ghost_obj_already_has_oid(self):
+ from persistent.interfaces import GHOST
+ candidate = self._makePersist(oid='123', state=GHOST)
+ cache = self._makeOne()
+ self.assertRaises(ValueError, cache.new_ghost, '123', candidate)
+
+ def test_new_ghost_obj_already_has_jar(self):
+ class Dummy(object):
+ _p_oid = None
+ _p_jar = object()
+ cache = self._makeOne()
+ candidate = self._makePersist(oid=None, jar=object())
+ self.assertRaises(ValueError, cache.new_ghost, '123', candidate)
+
+ def test_new_ghost_obj_already_in_cache(self):
+ cache = self._makeOne()
+ candidate = self._makePersist(oid=None, jar=None)
+ cache['123'] = candidate
+ self.assertRaises(KeyError, cache.new_ghost, '123', candidate)
+
+ def test_new_ghost_success_already_ghost(self):
+ from persistent.interfaces import GHOST
+ cache = self._makeOne()
+ candidate = self._makePersist(oid=None, jar=None)
+ cache.new_ghost('123', candidate)
+ self.failUnless(cache.get('123') is candidate)
+ self.assertEqual(candidate._p_oid, '123')
+ self.assertEqual(candidate._p_jar, cache.jar)
+ self.assertEqual(candidate._p_state, GHOST)
+
+ def test_new_ghost_success_not_already_ghost(self):
+ from persistent.interfaces import GHOST
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ candidate = self._makePersist(oid=None, jar=None, state=UPTODATE)
+ cache.new_ghost('123', candidate)
+ self.failUnless(cache.get('123') is candidate)
+ self.assertEqual(candidate._p_oid, '123')
+ self.assertEqual(candidate._p_jar, cache.jar)
+ self.assertEqual(candidate._p_state, GHOST)
+
+ def test_new_ghost_w_pclass_non_ghost(self):
+ class Pclass(object):
+ _p_oid = None
+ _p_jar = None
+ cache = self._makeOne()
+ cache.new_ghost('123', Pclass)
+ self.failUnless(cache.get('123') is Pclass)
+ self.failUnless(cache.persistent_classes['123'] is Pclass)
+ self.assertEqual(Pclass._p_oid, '123')
+ self.assertEqual(Pclass._p_jar, cache.jar)
+
+ def test_new_ghost_w_pclass_ghost(self):
+ class Pclass(object):
+ _p_oid = None
+ _p_jar = None
+ cache = self._makeOne()
+ cache.new_ghost('123', Pclass)
+ self.failUnless(cache.get('123') is Pclass)
+ self.failUnless(cache.persistent_classes['123'] is Pclass)
+ self.assertEqual(Pclass._p_oid, '123')
+ self.assertEqual(Pclass._p_jar, cache.jar)
+
+ def test_reify_miss_single(self):
+ cache = self._makeOne()
+ self.assertRaises(KeyError, cache.reify, '123')
+
+ def test_reify_miss_multiple(self):
+ cache = self._makeOne()
+ self.assertRaises(KeyError, cache.reify, ['123', '456'])
+
+ def test_reify_hit_single_ghost(self):
+ from persistent.interfaces import GHOST
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ candidate = self._makePersist(oid='123', jar=cache.jar, state=GHOST)
+ cache['123'] = candidate
+ self.assertEqual(cache.ringlen(), 0)
+ cache.reify('123')
+ self.assertEqual(cache.ringlen(), 1)
+ items = cache.lru_items()
+ self.assertEqual(items[0][0], '123')
+ self.failUnless(items[0][1] is candidate)
+ self.assertEqual(candidate._p_state, UPTODATE)
+
+ def test_reify_hit_single_non_ghost(self):
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ candidate = self._makePersist(oid='123', jar=cache.jar, state=UPTODATE)
+ cache['123'] = candidate
+ self.assertEqual(cache.ringlen(), 1)
+ cache.reify('123')
+ self.assertEqual(cache.ringlen(), 1)
+ self.assertEqual(candidate._p_state, UPTODATE)
+
+ def test_reify_hit_multiple_mixed(self):
+ from persistent.interfaces import GHOST
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ c1 = self._makePersist(oid='123', jar=cache.jar, state=GHOST)
+ cache['123'] = c1
+ c2 = self._makePersist(oid='456', jar=cache.jar, state=UPTODATE)
+ cache['456'] = c2
+ self.assertEqual(cache.ringlen(), 1)
+ cache.reify(['123', '456'])
+ self.assertEqual(cache.ringlen(), 2)
+ self.assertEqual(c1._p_state, UPTODATE)
+ self.assertEqual(c2._p_state, UPTODATE)
+
+ def test_invalidate_miss_single(self):
+ cache = self._makeOne()
+ cache.invalidate('123') # doesn't raise
+
+ def test_invalidate_miss_multiple(self):
+ cache = self._makeOne()
+ cache.invalidate(['123', '456']) # doesn't raise
+
+ def test_invalidate_hit_single_ghost(self):
+ from persistent.interfaces import GHOST
+ cache = self._makeOne()
+ candidate = self._makePersist(oid='123', jar=cache.jar, state=GHOST)
+ cache['123'] = candidate
+ self.assertEqual(cache.ringlen(), 0)
+ cache.invalidate('123')
+ self.assertEqual(cache.ringlen(), 0)
+ self.assertEqual(candidate._p_state, GHOST)
+
+ def test_invalidate_hit_single_non_ghost(self):
+ from persistent.interfaces import GHOST
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ candidate = self._makePersist(oid='123', jar=cache.jar, state=UPTODATE)
+ cache['123'] = candidate
+ self.assertEqual(cache.ringlen(), 1)
+ cache.invalidate('123')
+ self.assertEqual(cache.ringlen(), 0)
+ self.assertEqual(candidate._p_state, GHOST)
+
+ def test_invalidate_hit_multiple_mixed(self):
+ from persistent.interfaces import GHOST
+ from persistent.interfaces import UPTODATE
+ cache = self._makeOne()
+ c1 = self._makePersist(oid='123', jar=cache.jar, state=GHOST)
+ cache['123'] = c1
+ c2 = self._makePersist(oid='456', jar=cache.jar, state=UPTODATE)
+ cache['456'] = c2
+ self.assertEqual(cache.ringlen(), 1)
+ cache.invalidate(['123', '456'])
+ self.assertEqual(cache.ringlen(), 0)
+ self.assertEqual(c1._p_state, GHOST)
+ self.assertEqual(c2._p_state, GHOST)
+
+ def test_invalidate_hit_pclass(self):
+ class Pclass(object):
+ _p_oid = None
+ _p_jar = None
+ cache = self._makeOne()
+ cache['123'] = Pclass
+ self.failUnless(cache.persistent_classes['123'] is Pclass)
+ cache.invalidate('123')
+ self.failIf('123' in cache.persistent_classes)
+
+
class DummyPersistent(object):
- pass
+ def _p_invalidate(self):
+ from persistent.interfaces import GHOST
+ self._p_state = GHOST
+
+ def _p_activate(self):
+ from persistent.interfaces import UPTODATE
+ self._p_state = UPTODATE
+
+
class DummyConnection:
+ pass
- def setklassstate(self, obj):
- """Method used by PickleCache."""
def test_suite():
return unittest.TestSuite((
More information about the Zodb-checkins
mailing list