[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