Commit f0ac08a6 authored by Jason Madden's avatar Jason Madden

Add a test checking the non-cooperative behaviour of...

Add a test checking the non-cooperative behaviour of Persistent.__getattribute__ in both C and Python.
parent 2467abc9
...@@ -23,7 +23,7 @@ try: ...@@ -23,7 +23,7 @@ try:
from persistent.cPersistence import UPTODATE from persistent.cPersistence import UPTODATE
from persistent.cPersistence import CHANGED from persistent.cPersistence import CHANGED
from persistent.cPersistence import STICKY from persistent.cPersistence import STICKY
except ImportError: #pragma NO COVER except ImportError: # pragma: no cover
GHOST = -1 GHOST = -1
UPTODATE = 0 UPTODATE = 0
CHANGED = 1 CHANGED = 1
...@@ -314,10 +314,10 @@ class IPersistent(Interface): ...@@ -314,10 +314,10 @@ class IPersistent(Interface):
def _p_getattr(name): def _p_getattr(name):
"""Test whether the base class must handle the name """Test whether the base class must handle the name
The method unghostifies the object, if necessary. The method unghostifies the object, if necessary.
The method records the object access, if necessary. The method records the object access, if necessary.
This method should be called by subclass __getattribute__ This method should be called by subclass __getattribute__
implementations before doing anything else. If the method implementations before doing anything else. If the method
returns True, then __getattribute__ implementations must delegate returns True, then __getattribute__ implementations must delegate
...@@ -471,7 +471,7 @@ class IPickleCache(Interface): ...@@ -471,7 +471,7 @@ class IPickleCache(Interface):
""" Perform an incremental garbage collection sweep. """ Perform an incremental garbage collection sweep.
o Reduce number of non-ghosts to 'cache_size', if possible. o Reduce number of non-ghosts to 'cache_size', if possible.
o Ghostify in LRU order. o Ghostify in LRU order.
o Skip dirty or sticky objects. o Skip dirty or sticky objects.
...@@ -505,7 +505,7 @@ class IPickleCache(Interface): ...@@ -505,7 +505,7 @@ class IPickleCache(Interface):
If the object's '_p_jar' is not None, raise. If the object's '_p_jar' is not None, raise.
If 'oid' is already in the cache, raise. If 'oid' is already in the cache, raise.
""" """
def reify(to_reify): def reify(to_reify):
...@@ -536,7 +536,7 @@ class IPickleCache(Interface): ...@@ -536,7 +536,7 @@ class IPickleCache(Interface):
o Any OID corresponding to a p-class will cause the corresponding o Any OID corresponding to a p-class will cause the corresponding
p-class to be removed from the cache. p-class to be removed from the cache.
o For all other OIDs, ghostify the corrsponding object and o For all other OIDs, ghostify the corrsponding object and
remove it from the ring. remove it from the ring.
""" """
......
...@@ -48,7 +48,7 @@ _SWEEP_NEEDS_GC = not hasattr(sys, 'getrefcount') ...@@ -48,7 +48,7 @@ _SWEEP_NEEDS_GC = not hasattr(sys, 'getrefcount')
# On Jython, we need to explicitly ask it to monitor # On Jython, we need to explicitly ask it to monitor
# objects if we want a more deterministic GC # objects if we want a more deterministic GC
if hasattr(gc, 'monitorObject'): #pragma: no cover if hasattr(gc, 'monitorObject'): # pragma: no cover
_gc_monitor = gc.monitorObject _gc_monitor = gc.monitorObject
else: else:
def _gc_monitor(o): def _gc_monitor(o):
...@@ -395,8 +395,9 @@ class PickleCache(object): ...@@ -395,8 +395,9 @@ class PickleCache(object):
value = self.data.get(oid) value = self.data.get(oid)
if value is not None and value._p_state != GHOST: if value is not None and value._p_state != GHOST:
value._p_invalidate() value._p_invalidate()
if self.ring.delete(value): was_in_ring = self.ring.delete(value)
self.non_ghost_count -= 1 assert was_in_ring, "Ring corruption: invalidated a non-ghost not in ring"
self.non_ghost_count -= 1
elif oid in self.persistent_classes: elif oid in self.persistent_classes:
persistent_class = self.persistent_classes[oid] persistent_class = self.persistent_classes[oid]
del self.persistent_classes[oid] del self.persistent_classes[oid]
......
...@@ -146,7 +146,7 @@ class _DequeRing(object): ...@@ -146,7 +146,7 @@ class _DequeRing(object):
try: try:
from cffi import FFI from cffi import FFI
except ImportError: #pragma: no cover except ImportError: # pragma: no cover
_CFFIRing = None _CFFIRing = None
else: else:
......
...@@ -692,6 +692,24 @@ class _Persistent_Base(object): ...@@ -692,6 +692,24 @@ class _Persistent_Base(object):
self.assertEqual(getattr(inst, 'normal', None), 'value') self.assertEqual(getattr(inst, 'normal', None), 'value')
self._checkMRU(jar, [OID]) self._checkMRU(jar, [OID])
def test___getattribute___non_cooperative(self):
# Getting attributes is NOT cooperative with the superclass.
# This comes from the C implementation and is maintained
# for backwards compatibility. (For example, Persistent and
# ExtensionClass.Base/Acquisition take special care to mix together.)
class Base(object):
def __getattribute__(self, name):
if name == 'magic':
return 42
return super(Base,self).__getattribute__(name)
self.assertEqual(getattr(Base(), 'magic'), 42)
class Derived(self._getTargetClass(), Base):
pass
self.assertRaises(AttributeError, getattr, Derived(), 'magic')
def test___setattr___p__names(self): def test___setattr___p__names(self):
from persistent.timestamp import _makeOctets from persistent.timestamp import _makeOctets
SERIAL = _makeOctets('\x01' * 8) SERIAL = _makeOctets('\x01' * 8)
......
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