[Checkins] SVN: zope.app.principalannotation/trunk/ Fix LP:237985

Dan Korostelev nadako at gmail.com
Sun Dec 7 14:12:34 EST 2008


Log message for revision 93756:
  Fix LP:237985

Changed:
  U   zope.app.principalannotation/trunk/CHANGES.txt
  U   zope.app.principalannotation/trunk/setup.py
  U   zope.app.principalannotation/trunk/src/zope/app/principalannotation/__init__.py
  U   zope.app.principalannotation/trunk/src/zope/app/principalannotation/tests.py

-=-
Modified: zope.app.principalannotation/trunk/CHANGES.txt
===================================================================
--- zope.app.principalannotation/trunk/CHANGES.txt	2008-12-07 17:41:06 UTC (rev 93755)
+++ zope.app.principalannotation/trunk/CHANGES.txt	2008-12-07 19:12:34 UTC (rev 93756)
@@ -2,6 +2,13 @@
 CHANGES
 =======
 
+3.4.1 (unreleased)
+
+- Fix the case described in LP:#237985, when two unstored
+  Annotations are got and then tried to store itself, overriding
+  each other. This is done by caching unstored objects on the
+  utility.
+
 3.4.0 (2007-10-26)
 ------------------
 

Modified: zope.app.principalannotation/trunk/setup.py
===================================================================
--- zope.app.principalannotation/trunk/setup.py	2008-12-07 17:41:06 UTC (rev 93755)
+++ zope.app.principalannotation/trunk/setup.py	2008-12-07 19:12:34 UTC (rev 93756)
@@ -22,7 +22,7 @@
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
 setup(name='zope.app.principalannotation',
-      version = '3.4.0',
+      version = '3.4.1dev',
       author='Zope Corporation and Contributors',
       author_email='zope3-dev at zope.org',
       description='Annotations for Zope Principals',

Modified: zope.app.principalannotation/trunk/src/zope/app/principalannotation/__init__.py
===================================================================
--- zope.app.principalannotation/trunk/src/zope/app/principalannotation/__init__.py	2008-12-07 17:41:06 UTC (rev 93755)
+++ zope.app.principalannotation/trunk/src/zope/app/principalannotation/__init__.py	2008-12-07 19:12:34 UTC (rev 93756)
@@ -42,6 +42,15 @@
     def __init__(self):
         self.annotations = OOBTree()
 
+    def __getstate__(self):
+        # We can't use the _v_ attribute because we surely don't want
+        # it to be wiped out from the cache, so we just exclude our
+        # attribute from object's state :)
+        state = super(PrincipalAnnotationUtility, self).__getstate__()
+        if '_newAnnotationsCache' in state:
+            del state['_newAnnotationsCache']
+        return state
+
     def getAnnotations(self, principal):
         """Return object implementing IAnnotations for the given principal.
 
@@ -57,9 +66,15 @@
 
         annotations = self.annotations.get(principalId)
         if annotations is None:
-            annotations = Annotations(principalId, store=self.annotations)
-            annotations.__parent__ = self
-            annotations.__name__ = principalId
+            if not hasattr(self, '_newAnnotationsCache'):
+                self._newAnnotationsCache = {}
+            if principalId in self._newAnnotationsCache:
+                return self._newAnnotationsCache[principalId]
+            else:
+                annotations = Annotations(principalId, store=self.annotations, cache=self._newAnnotationsCache)
+                annotations.__parent__ = self
+                annotations.__name__ = principalId
+                self._newAnnotationsCache[principalId] = annotations
 
         return annotations
 
@@ -73,13 +88,14 @@
 
     interface.implements(IAnnotations)
 
-    def __init__(self, principalId, store=None):
+    def __init__(self, principalId, store=None, cache=None):
         self.principalId = principalId
         self.data = PersistentDict() # We don't really expect that many
 
         # _v_store is used to remember a mapping object that we should
         # be saved in if we ever change
         self._v_store = store
+        self._v_cache = cache
 
     def __getitem__(self, key):
         try:
@@ -98,6 +114,9 @@
             # be saved in if we ever change
             self._v_store[self.principalId] = self
             del self._v_store
+            if getattr(self, '_v_cache', None) is not None:
+                del self._v_cache[self.principalId]
+                del self._v_cache
 
         self.data[key] = value
 

Modified: zope.app.principalannotation/trunk/src/zope/app/principalannotation/tests.py
===================================================================
--- zope.app.principalannotation/trunk/src/zope/app/principalannotation/tests.py	2008-12-07 17:41:06 UTC (rev 93755)
+++ zope.app.principalannotation/trunk/src/zope/app/principalannotation/tests.py	2008-12-07 19:12:34 UTC (rev 93756)
@@ -56,6 +56,37 @@
         # But now we should have the annotation:
         self.assert_(self.util.hasAnnotations(prince))
 
+        # Another scenery, say we get annotations two
+        # times.
+        king = Principal('somebody2')
+        self.assert_(not self.util.hasAnnotations(king))
+
+        kingAnnotation = self.util.getAnnotations(king)
+        self.assert_(not self.util.hasAnnotations(king))
+        
+        kingAnnotation2 = self.util.getAnnotations(king)
+        self.assert_(not self.util.hasAnnotations(king))
+
+        # These objects should be the same
+        self.assert_(kingAnnotation is kingAnnotation2)
+
+        # So this should work:
+        kingAnnotation['something'] = 'whatever'
+        self.assert_(self.util.hasAnnotations(king))
+
+        kingAnnotation2['something2'] = 'whatever2'
+        self.assert_(self.util.hasAnnotations(king))
+        
+        # Let's check the results:
+        annot = self.util.getAnnotations(king)
+        self.assert_(annot.get('something2'))
+        self.assert_(annot.get('something'))
+        
+        # This is done using the internal cache object in the utility,
+        # but it should not be stored in the database, so it won't be
+        # in object's state:
+        self.assertEquals(self.util.__getstate__().keys(), ['annotations'])
+        
     def testGetFromLayered(self):
         princeSomebody = Principal('somebody')
         sm1 = self.makeSite('folder1')



More information about the Checkins mailing list