[Checkins] SVN: zope.principalannotation/trunk/ Initial import. The previous checkin was borked.

Dan Korostelev nadako at gmail.com
Mon Mar 9 10:32:47 EDT 2009


Log message for revision 97693:
  Initial import. The previous checkin was borked.

Changed:
  U   zope.principalannotation/trunk/buildout.cfg
  U   zope.principalannotation/trunk/setup.py
  A   zope.principalannotation/trunk/src/zope/principalannotation/README.txt
  U   zope.principalannotation/trunk/src/zope/principalannotation/tests.py
  A   zope.principalannotation/trunk/src/zope/principalannotation/utility.py

-=-
Modified: zope.principalannotation/trunk/buildout.cfg
===================================================================
--- zope.principalannotation/trunk/buildout.cfg	2009-03-09 14:09:35 UTC (rev 97692)
+++ zope.principalannotation/trunk/buildout.cfg	2009-03-09 14:32:47 UTC (rev 97693)
@@ -1,6 +1,6 @@
 [buildout]
-develop = .
-parts = test coverage-test coverage-report
+develop = . ../zope.site
+parts = test coverage-test coverage-report docs
 
 [test]
 recipe = zc.recipe.testrunner
@@ -16,3 +16,11 @@
 eggs = z3c.coverage
 scripts = coverage=coverage-report
 arguments = ('coverage', 'coverage/report')
+
+[docs]
+recipe = z3c.recipe.sphinxdoc
+eggs = zope.principalannotation [docs]
+build-dir = ${buildout:directory}/docs
+index-doc = README
+default.css =
+layout.html =

Modified: zope.principalannotation/trunk/setup.py
===================================================================
--- zope.principalannotation/trunk/setup.py	2009-03-09 14:09:35 UTC (rev 97692)
+++ zope.principalannotation/trunk/setup.py	2009-03-09 14:32:47 UTC (rev 97693)
@@ -47,7 +47,8 @@
       packages=find_packages('src'),
       package_dir = {'': 'src'},
       namespace_packages=['zope'],
-      extras_require = dict(test=['zope.testing', 'zope.site[test]']),
+      extras_require = dict(test=['zope.testing', 'zope.site[test]'],
+                            docs=['z3c.recipe.sphinxdoc',]),
       install_requires=['setuptools',
                         'ZODB3',
                         'zope.annotation',

Added: zope.principalannotation/trunk/src/zope/principalannotation/README.txt
===================================================================
--- zope.principalannotation/trunk/src/zope/principalannotation/README.txt	                        (rev 0)
+++ zope.principalannotation/trunk/src/zope/principalannotation/README.txt	2009-03-09 14:32:47 UTC (rev 97693)
@@ -0,0 +1,213 @@
+=====================
+Principal Annotations
+=====================
+
+This package implements annotations for zope.security principals.
+To make it clear, the `principal` here is the object that provides
+``zope.security.interfaces.IPrincipal`` interface and `annotations` is
+the object providing ``zope.annotation.interfaces.IAnnotations``.
+
+The problem is that principals is dynamic, non-persistent objects created
+on the fly for every security participation (request or something), so
+common annotation techniques, like AttributeAnnotations cannot be applied
+to them.
+
+This package provides a persistent storage of principal annotations,
+storing annotations by principal ID as well as an adapter from IPrincipal
+to IAnnotations.
+
+
+PrincipalAnnotationUtility
+--------------------------
+
+The core of this package is the ``PrincipalAnnotationUtility`` class
+that stores annotations for principals and allows to get them easily.
+
+It provides the IPrincipalAnnotationUtility interface::
+
+  >>> from zope.principalannotation.interfaces import IPrincipalAnnotationUtility
+  >>> from zope.principalannotation.utility import PrincipalAnnotationUtility
+  >>> from zope.interface.verify import verifyObject
+  >>> util = PrincipalAnnotationUtility()
+  >>> verifyObject(IPrincipalAnnotationUtility, util)
+  True
+
+It provides three methods: ``getAnnotations``, ``getAnnotationsById``
+and ``hasAnnotations``. Let's create a testing principal and check out
+these methods::
+
+  >>> from zope.security.testing import Principal
+  >>> nadako = Principal('nadako')
+  >>> nadako.id
+  'nadako'
+
+We can check if our principal has any annotations. Of course, it
+currently doesn't have any::
+
+  >>> util.hasAnnotations(nadako)
+  False
+
+We can get ``IAnnotations`` object using principal object itself::
+
+  >>> util.getAnnotations(nadako)
+  <zope.principalannotation.utility.Annotations object at 0x...>
+
+Or using principal id::
+
+  >>> util.getAnnotationsById(nadako.id)
+  <zope.principalannotation.utility.Annotations object at 0x...>
+
+Let's get the ``IAnnotations`` object for our principal and play with it::
+
+  >>> annots = util.getAnnotations(nadako)
+
+  >>> from zope.interface.verify import verifyObject
+  >>> from zope.annotation.interfaces import IAnnotations
+  >>> verifyObject(IAnnotations, annots)
+  True
+
+Let's check the ``IAnnotation`` contract::
+
+  >>> bool(annots)
+  False
+
+  >>> annots['not.here']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'not.here'
+
+  >>> annots.get('not.here') is None
+  True
+
+  >>> annots.get('not.here', 42)
+  42
+
+Note, that the ``IAnnotations`` object gets stored in the utility only
+when we set a key for it. This is a simple optimization that allows
+us not to store any data when all we do is simply checking for presense
+of annotation. The ``hasAnnotations`` method will return ``True`` after
+storing a key in the annotations::
+
+  >>> util.hasAnnotations(nadako)
+  False
+
+  >>> annots['its.here'] = 'some info'
+
+  >>> util.hasAnnotations(nadako)
+  True
+
+We can also delete the existing key::
+
+  >>> del annots['its.here']
+
+But we can't delete the key that is (no more) existant::
+
+  >>> del annots['its.here']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'its.here'
+
+
+Multiple annotation utilities
+-----------------------------
+
+Imagine that your application has a root ``site`` object with its
+component registry (a.k.a. site manager) and that object has a sub-site
+object with its own component registry, and that component registry
+has the root's component registry as its base.
+
+In that case, we want the ``IAnnotations`` object to be available to
+retrieve annotations from higher-level utilities.
+
+Let's register our utility in the root site and create a sub-site
+with its own IPrincipalAnnotationUtility::
+
+  >>> root['util'] = util
+  >>> rootsm = root.getSiteManager()
+  >>> rootsm.registerUtility(util, IPrincipalAnnotationUtility)
+
+  >>> from zope.site.folder import Folder
+  >>> from zope.site.site import LocalSiteManager
+
+  >>> subsite = Folder()
+  >>> root['subsite'] = subsite
+  >>> subsm = LocalSiteManager(subsite)
+  >>> subsite.setSiteManager(subsm)
+
+  >>> util2 = PrincipalAnnotationUtility()
+  >>> subsite['util2'] = util2
+  >>> subsm.registerUtility(util2, IPrincipalAnnotationUtility)
+
+Now, let's create a key in the IAnnotations, provided by root utility::
+
+  >>> annots = util.getAnnotations(nadako)
+  >>> annots['root.number'] = 42
+
+The subsite utility should get the annotation successfully::
+
+  >>> annots2 = util2.getAnnotations(nadako)
+  >>> bool(annots2)
+  True
+
+  >>> annots2['root.number']
+  42
+
+If we have the key both in higher-level annotations and lower-level ones,
+the lower-level will have priority, but higher-level won't be deleted or
+overriden::
+
+  >>> annots['another.number'] = 1
+  >>> annots2['another.number'] = 42
+
+  >>> annots['another.number']
+  1
+  >>> annots2['another.number']
+  42
+
+If we'll delete the key from lower-level, it will not be deleted from a
+higher level utility::
+
+  >>> del annots2['another.number']
+
+  >>> annots['another.number']
+  1
+  >>> annots2['another.number']
+  1
+
+
+IPrincipal -> IAnnotations adapter
+----------------------------------
+
+Of course, the most nice feature is that we can simply adapt our
+principal object to IAnnotations and get those annotations using
+standard way documented in ``zope.annotation`` package.
+
+  >>> annots = IAnnotations(nadako)
+  >>> annots
+  <zope.principalannotation.utility.Annotations object at 0x...>
+  >>> annots['root.number']
+  42
+
+By default, the IAnnotation adapter uses the current site's utility::
+
+  >>> IAnnotations(nadako) is util.getAnnotations(nadako)
+  True
+
+  >>> from zope.site.hooks import setSite
+  >>> setSite(subsite)
+  
+  >>> IAnnotations(nadako) is util2.getAnnotations(nadako)
+  True
+
+Howerver, we can use a binary multi-adapter to IAnnotations to specify
+some context object from which to get the annotations utility::
+
+  >>> from zope.component import getMultiAdapter
+  
+  >>> annots = getMultiAdapter((nadako, root), IAnnotations)
+  >>> annots is util.getAnnotations(nadako)
+  True
+
+  >>> annots = getMultiAdapter((nadako, subsite), IAnnotations)
+  >>> annots is util2.getAnnotations(nadako)
+  True

Modified: zope.principalannotation/trunk/src/zope/principalannotation/tests.py
===================================================================
--- zope.principalannotation/trunk/src/zope/principalannotation/tests.py	2009-03-09 14:09:35 UTC (rev 97692)
+++ zope.principalannotation/trunk/src/zope/principalannotation/tests.py	2009-03-09 14:32:47 UTC (rev 97693)
@@ -40,83 +40,3 @@
             setUp=setUp, tearDown=tearDown,
             optionflags=doctest.ELLIPSIS)
         ))
-
-
-'''
-from unittest import TestCase, TestLoader, TextTestRunner
-
-import zope.component
-from zope.interface import implements
-from zope.security.interfaces import IPrincipal
-
-from zope.app.component.testing import PlacefulSetup
-from zope.app.testing import setup
-
-from zope.principalannotation.interfaces import IPrincipalAnnotationUtility
-from zope.principalannotation.utility import PrincipalAnnotationUtility
-
-class Principal(object):
-    implements(IPrincipal)
-
-    def __init__(self, id):
-        self.id = id
-
-
-class PrincipalAnnotationTests(PlacefulSetup, TestCase):
-
-    def setUp(self):
-        PlacefulSetup.setUp(self)
-        sm = self.buildFolders(site='/')
-        self.util = PrincipalAnnotationUtility()
-        zope.component.provideUtility(self.util, IPrincipalAnnotationUtility)
-
-    def testGetSimple(self):
-        prince = Principal('somebody')
-        self.assert_(not self.util.hasAnnotations(prince))
-
-        princeAnnotation = self.util.getAnnotations(prince)
-        # Just getting doesn't actualy store. We don't want to store unless
-        # we make a change.
-        self.assert_(not self.util.hasAnnotations(prince))
-
-        princeAnnotation['something'] = 'whatever'
-
-        # But now we should have the annotation:
-        self.assert_(self.util.hasAnnotations(prince))
-
-    def testGetFromLayered(self):
-        princeSomebody = Principal('somebody')
-        sm1 = self.makeSite('folder1')
-        subUtil = setup.addUtility(sm1, '', IPrincipalAnnotationUtility,
-                                   PrincipalAnnotationUtility())
-
-        parentAnnotation = self.util.getAnnotations(princeSomebody)
-
-        # Just getting doesn't actualy store. We don't want to store unless
-        # we make a change.
-        self.assert_(not subUtil.hasAnnotations(princeSomebody))
-
-        parentAnnotation['hair_color'] = 'blue'
-
-        # But now we should have the annotation:
-        self.assert_(self.util.hasAnnotations(princeSomebody))
-
-        subAnnotation = subUtil.getAnnotations(princeSomebody)
-        self.assertEquals(subAnnotation['hair_color'], 'blue')
-
-        subAnnotation['foo'] = 'bar'
-
-        self.assertEquals(parentAnnotation.get("foo"), None)
-
-def test_suite():
-    loader=TestLoader()
-    tests = loader.loadTestsFromTestCase(PrincipalAnnotationTests)
-    import zope.principalannotation.utility
-    tests.addTest(doctest.DocTestSuite(zope.principalannotation.utility,
-                                       setUp=setup.placelessSetUp,
-                                       tearDown=setup.placelessTearDown))
-    return tests
-
-if __name__=='__main__':
-    TextTestRunner().run(test_suite())
-'''
\ No newline at end of file

Added: zope.principalannotation/trunk/src/zope/principalannotation/utility.py
===================================================================
--- zope.principalannotation/trunk/src/zope/principalannotation/utility.py	                        (rev 0)
+++ zope.principalannotation/trunk/src/zope/principalannotation/utility.py	2009-03-09 14:32:47 UTC (rev 97693)
@@ -0,0 +1,124 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Implementation of IPrincipalAnnotationUtility
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+from BTrees.OOBTree import OOBTree
+from persistent import Persistent
+from persistent.dict import PersistentDict
+from zope import interface, component
+from zope.annotation.interfaces import IAnnotations
+from zope.container.contained import Contained
+from zope.location import Location
+from zope.security.interfaces import IPrincipal
+from zope.site.next import queryNextUtility
+   
+from zope.principalannotation.interfaces import IPrincipalAnnotationUtility
+   
+# TODO: register utility as adapter for IAnnotations on utility activation.
+   
+class PrincipalAnnotationUtility(Persistent, Contained):
+    """Stores `IAnnotations` for `IPrinicipals`.
+    
+    The utility ID is 'PrincipalAnnotation'.
+    """
+   
+    interface.implements(IPrincipalAnnotationUtility)
+   
+    def __init__(self):
+        self.annotations = OOBTree()
+
+    def getAnnotations(self, principal):
+        """Return object implementing IAnnotations for the given principal.
+        
+        If there is no `IAnnotations` it will be created and then returned.
+        """
+        return self.getAnnotationsById(principal.id)
+   
+    def getAnnotationsById(self, principalId):
+        """Return object implementing `IAnnotations` for the given principal.
+   
+        If there is no `IAnnotations` it will be created and then returned.
+        """
+        annotations = self.annotations.get(principalId)
+        if annotations is None:
+            annotations = Annotations(principalId, store=self.annotations)
+            annotations.__parent__ = self
+            annotations.__name__ = principalId
+        return annotations
+
+    def hasAnnotations(self, principal):
+        """Return boolean indicating if given principal has `IAnnotations`."""
+        return principal.id in self.annotations
+
+class Annotations(Persistent, Location):
+    """Stores annotations."""
+
+    interface.implements(IAnnotations)
+
+    def __init__(self, principalId, store=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
+
+    def __nonzero__(self):
+        nz = bool(self.data)
+        if not nz:
+            # maybe higher-level utility's annotations will be non-zero
+            next = queryNextUtility(self, IPrincipalAnnotationUtility)
+            if next is not None:
+                annotations = next.getAnnotationsById(self.principalId)
+                return bool(next)
+        return nz
+
+    def __getitem__(self, key):
+        try:
+            return self.data[key]
+        except KeyError:
+            # We failed locally: delegate to a higher-level utility.
+            next = queryNextUtility(self, IPrincipalAnnotationUtility)
+            if next is not None:
+                annotations = next.getAnnotationsById(self.principalId)
+                return annotations[key]
+            raise
+
+    def get(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+    def __setitem__(self, key, value):
+        if getattr(self, '_v_store', None) is not None:
+            # _v_store is used to remember a mapping object that we should
+            # be saved in if we ever change
+            self._v_store[self.principalId] = self
+            del self._v_store
+        
+        self.data[key] = value
+
+    def __delitem__(self, key):
+        del self.data[key]
+
+   
+ at component.adapter(IPrincipal)
+ at interface.implementer(IAnnotations)
+def annotations(principal, context=None):
+    utility = component.getUtility(IPrincipalAnnotationUtility, context=context)
+    return utility.getAnnotations(principal)


Property changes on: zope.principalannotation/trunk/src/zope/principalannotation/utility.py
___________________________________________________________________
Added: svn:keywords
   + Id



More information about the Checkins mailing list