[Zope3-checkins] SVN: Zope3/trunk/ Convenience factory to register annotations. Also modified tests a bit

Martijn Faassen faassen at infrae.com
Thu Mar 16 14:06:21 EST 2006


Log message for revision 66061:
  Convenience factory to register annotations. Also modified tests a bit
  and added README.txt doctest.
  

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  A   Zope3/trunk/src/zope/app/annotation/README.txt
  U   Zope3/trunk/src/zope/app/annotation/__init__.py
  A   Zope3/trunk/src/zope/app/annotation/_factory.py
  U   Zope3/trunk/src/zope/app/annotation/tests/annotations.py
  U   Zope3/trunk/src/zope/app/annotation/tests/test_attributeannotations.py

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2006-03-16 17:20:08 UTC (rev 66060)
+++ Zope3/trunk/doc/CHANGES.txt	2006-03-16 19:06:20 UTC (rev 66061)
@@ -33,6 +33,10 @@
         "pkg_resources" is available. This will enable distribution of
         "zope.*" packages as individual eggs.
 
+      - A new zope.app.annotation.factory helper function that makes
+        it easier to create annotations. Also added a README in
+        zope.app.annotation which explains how to use it.
+
     Restructuring
 
       - Removed 'Adding.renderAddButton' method which had been

Added: Zope3/trunk/src/zope/app/annotation/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/annotation/README.txt	2006-03-16 17:20:08 UTC (rev 66060)
+++ Zope3/trunk/src/zope/app/annotation/README.txt	2006-03-16 19:06:20 UTC (rev 66061)
@@ -0,0 +1,94 @@
+Annotations
+===========
+
+There is more to document about annotations, but we'll just sketch out
+a scenario on how to use the annotation factory for now. This is one
+of the easiest ways to use annotations -- basically you can see them
+as persistent, writeable adapters.
+
+First, let's make a persistent object we can create annotations for:
+
+  >>> from zope import interface
+  >>> class IFoo(interface.Interface):
+  ...     pass
+  >>> from zope.app.annotation.interfaces import IAttributeAnnotatable
+  >>> from persistent import Persistent
+  >>> class Foo(Persistent):
+  ...     interface.implements(IFoo, IAttributeAnnotatable)
+
+We directly say that Foo implements IAttributeAnnotatable here. In
+practice this is often done in ZCML, using the `implements`
+subdirective of the `content` or `class` directive.
+
+Now let's create an annotation for this:
+  
+  >>> class IBar(interface.Interface):
+  ...     a = interface.Attribute('A')
+  ...     b = interface.Attribute('B')
+  >>> from zope import component
+  >>> class Bar(Persistent):
+  ...     interface.implements(IBar)
+  ...     component.adapts(IFoo)
+  ...     def __init__(self):
+  ...         self.a = 1
+  ...         self.b = 2
+
+Note that the annotation implementation does not expect any arguments
+to its `__init__`. Otherwise it's basically an adapter.
+
+Now, we'll register the annotation as an adapter. Do do this we use
+the `factory` function provided by `zope.app.annotation`:
+
+  >>> from zope.app.annotation import factory
+  >>> component.provideAdapter(factory(Bar))
+
+Note that we do not need to specify what the adapter provides or what
+it adapts - we already do this on the annotation class itself.
+
+Now we let's make an instance of `Foo`, and make an annotation for it.
+
+  >>> foo = Foo()
+  >>> bar = IBar(foo)
+  >>> bar.a
+  1
+  >>> bar.b
+  2
+
+We'll change `a` and get the annotation again. Our change is still
+there:
+
+  >>> bar.a = 3
+  >>> IBar(foo).a
+  3
+
+Of course it's still different for another instance of `Foo`:
+
+  >>> foo2 = Foo()
+  >>> IBar(foo2).a
+  1
+
+What if our annotation does not provide what it adapts with
+`component.adapts`? It will complain:
+
+  >>> class IQux(interface.Interface):
+  ...     pass
+  >>> class Qux(Persistent):
+  ...     interface.implements(IQux)
+  >>> component.provideAdapter(factory(Qux)) # doctest: +ELLIPSIS
+  Traceback (most recent call last):
+  ...
+  TypeError: Missing 'zope.component.adapts' on annotation
+
+It's possible to provide an annotation with an explicit key. (If the
+key is not supplied, the key is deduced from the anotation's dotted
+name, provided it is a class.)
+
+  >>> class IHoi(interface.Interface):
+  ...     pass
+  >>> class Hoi(Persistent):
+  ...     interface.implements(IHoi)
+  ...     component.adapts(IFoo)
+  >>> component.provideAdapter(factory(Hoi, 'my.unique.key'))
+  >>> isinstance(IHoi(foo), Hoi)
+  True
+

Modified: Zope3/trunk/src/zope/app/annotation/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/annotation/__init__.py	2006-03-16 17:20:08 UTC (rev 66060)
+++ Zope3/trunk/src/zope/app/annotation/__init__.py	2006-03-16 19:06:20 UTC (rev 66061)
@@ -18,3 +18,4 @@
 
 from zope.app.annotation.interfaces import IAttributeAnnotatable
 from zope.app.annotation.interfaces import IAnnotations
+from zope.app.annotation._factory import factory

Added: Zope3/trunk/src/zope/app/annotation/_factory.py
===================================================================
--- Zope3/trunk/src/zope/app/annotation/_factory.py	2006-03-16 17:20:08 UTC (rev 66060)
+++ Zope3/trunk/src/zope/app/annotation/_factory.py	2006-03-16 19:06:20 UTC (rev 66061)
@@ -0,0 +1,30 @@
+import zope.component
+import zope.interface
+from zope.app.annotation.interfaces import IAnnotations
+import zope.app.container.contained
+
+def factory(factory, key=None):
+    """Adapter factory to help create annotations easily.
+    """
+    # if no key is provided,
+    # we'll determine the unique key based on the factory's dotted name
+    if key is None:
+        key = factory.__module__ + '.' + factory.__name__
+
+    adapts = zope.component.adaptedBy(factory)
+    if adapts is None:
+        raise TypeError("Missing 'zope.component.adapts' on annotation")
+
+    @zope.component.adapter(list(adapts)[0])
+    @zope.interface.implementer(list(zope.component.implementedBy(factory))[0])
+    def getAnnotation(context):
+        annotations = IAnnotations(context)
+        try:
+            return annotations[key]
+        except KeyError:
+            result = factory()
+            annotations[key] = result
+            zope.app.container.contained.contained(
+                result, context, key)
+            return result
+    return getAnnotation

Modified: Zope3/trunk/src/zope/app/annotation/tests/annotations.py
===================================================================
--- Zope3/trunk/src/zope/app/annotation/tests/annotations.py	2006-03-16 17:20:08 UTC (rev 66060)
+++ Zope3/trunk/src/zope/app/annotation/tests/annotations.py	2006-03-16 19:06:20 UTC (rev 66061)
@@ -22,10 +22,10 @@
 from zope.interface.verify import verifyObject
 from zope.app.annotation.interfaces import IAnnotations
 
-class IAnnotationsTest(unittest.TestCase):
+class AnnotationsTest(unittest.TestCase):
     """Test the IAnnotations interface.
 
-    The test case class expects the 'IAnnotations' to be in
+    The test case class expects the 'IAnnotations' implementer to be in
     'self.annotations'.
     """
 

Modified: Zope3/trunk/src/zope/app/annotation/tests/test_attributeannotations.py
===================================================================
--- Zope3/trunk/src/zope/app/annotation/tests/test_attributeannotations.py	2006-03-16 17:20:08 UTC (rev 66060)
+++ Zope3/trunk/src/zope/app/annotation/tests/test_attributeannotations.py	2006-03-16 19:06:20 UTC (rev 66061)
@@ -11,29 +11,45 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Tests the 'AttributeAnnotations' adapter.
+"""Tests the 'AttributeAnnotations' adapter. Also test the annotation
+factory.
 
 $Id$
 """
-from unittest import main, makeSuite
-from zope.testing.cleanup import CleanUp # Base class w registry cleanup
-from zope.app.annotation.tests.annotations import IAnnotationsTest
+import unittest, doctest
+from zope.testing import cleanup
+from zope.app.annotation.tests.annotations import AnnotationsTest
 from zope.app.annotation.attribute import AttributeAnnotations
 from zope.app.annotation.interfaces import IAttributeAnnotatable
 from zope.interface import implements
+from zope import component
 
 class Dummy(object):
     implements(IAttributeAnnotatable)
 
-class AttributeAnnotationsTest(IAnnotationsTest, CleanUp):
+class AttributeAnnotationsTest(AnnotationsTest, cleanup.CleanUp):
 
     def setUp(self):
         self.annotations = AttributeAnnotations(Dummy())
         super(AttributeAnnotationsTest, self).setUp()
 
 
+def setUp(test=None):
+    cleanup.setUp()
+    component.provideAdapter(AttributeAnnotations)
+    
+def tearDown(test=None):
+    cleanup.tearDown()
+    
 def test_suite():
-    return makeSuite(AttributeAnnotationsTest)
+    return unittest.TestSuite((
+        unittest.makeSuite(AttributeAnnotationsTest),
+        doctest.DocFileSuite('../README.txt', setUp=setUp, tearDown=tearDown)
+        ))
 
+    #return makeSuite(AttributeAnnotationsTest)
+    #    doctest.DocFileSuite('README.txt',
+    #                         setUp=setUp, tearDown=tearDown),
+
 if __name__=='__main__':
-    main(defaultTest='test_suite')
+    unittest.main(defaultTest='test_suite')



More information about the Zope3-Checkins mailing list