[Checkins] SVN: grok/trunk/ Implement annotations,
pretty much according to the original design notes.
Philipp von Weitershausen
philikon at philikon.de
Fri Jan 19 18:35:03 EST 2007
Log message for revision 72111:
Implement annotations, pretty much according to the original design notes.
You inherit from grok.Annotation. The resulting class can be thought of as a
persistent adapter. It gets instantiated the first time an object is adapted
and then persisted as part of the object. Like with adapters, use grok.context,
grok.implements/grok.provides to specify what's adapted and what's provided.
Use grok.name to specify the annotation key, which is always a good idea if
you anticipate moving the class around, because otherwise the annotation key
will be the dotted class path.
Changed:
U grok/trunk/TODO.txt
U grok/trunk/doc/design/annotations.py
U grok/trunk/src/grok/__init__.py
U grok/trunk/src/grok/components.py
U grok/trunk/src/grok/interfaces.py
U grok/trunk/src/grok/meta.py
A grok/trunk/src/grok/tests/annotation/
A grok/trunk/src/grok/tests/annotation/__init__.py
A grok/trunk/src/grok/tests/annotation/annotation.py
A grok/trunk/src/grok/tests/annotation/implementsmany.py
A grok/trunk/src/grok/tests/annotation/implementsnone.py
A grok/trunk/src/grok/tests/annotation/name.py
A grok/trunk/src/grok/tests/annotation/provides.py
U grok/trunk/src/grok/tests/test_grok.py
-=-
Modified: grok/trunk/TODO.txt
===================================================================
--- grok/trunk/TODO.txt 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/TODO.txt 2007-01-19 23:35:02 UTC (rev 72111)
@@ -13,8 +13,6 @@
- choice fields / sources (theuni)
-- annotations (faassen)
-
- testing strategy for the tutorial (faassen)
- make it easier to write tests (wosc, faassen)
Modified: grok/trunk/doc/design/annotations.py
===================================================================
--- grok/trunk/doc/design/annotations.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/doc/design/annotations.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -1,14 +1,24 @@
import grok
+from zope import interface
+from BTrees.OOBTree import OOTreeSet
class Article(grok.Model):
pass
+class IComments(interface.Interface):
+
+ def addComment(text):
+ pass
+
+ def getComments():
+ pass
+
class Comments(grok.Annotation):
grok.context(Article) # this is actually the default
grok.implements(IComments)
+ grok.name('annotations.Comments') # this is actually the default
def __init__(self):
- # XXX need super?!?
self.comments = OOTreeSet()
def addComment(self, text):
Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/__init__.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -32,7 +32,7 @@
from grok.components import ClassGrokker, InstanceGrokker, ModuleGrokker
from grok.components import Model, Adapter, MultiAdapter, View, XMLRPC
from grok.components import PageTemplate, PageTemplateFile, Container, Traverser
-from grok.components import Site, GlobalUtility, LocalUtility
+from grok.components import Site, GlobalUtility, LocalUtility, Annotation
from grok.components import EditForm, DisplayForm, AddForm
from grok.directive import (context, name, template, templatedir, provides,
baseclass, global_utility, local_utility,
Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/components.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -32,6 +32,7 @@
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.browser.absoluteurl import AbsoluteURL
from zope.traversing.browser.absoluteurl import _safe as SAFE_URL_CHARACTERS
+from zope.annotation.interfaces import IAttributeAnnotatable
from zope.app.pagetemplate.engine import TrustedAppPT
from zope.app.publisher.browser import getDefaultViewName
@@ -99,7 +100,7 @@
# XXX Inheritance order is important here. If we reverse this,
# then containers can't be models anymore because no unambigous MRO
# can be established.
- pass
+ interface.implements(IAttributeAnnotatable)
class Container(BTreeContainer):
@@ -128,6 +129,10 @@
pass
+class Annotation(persistent.Persistent):
+ pass
+
+
class View(BrowserPage):
interface.implements(interfaces.IGrokView)
Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/interfaces.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -28,6 +28,7 @@
Site = interface.Attribute("Mixin class for sites.")
Adapter = interface.Attribute("Base class for adapters.")
MultiAdapter = interface.Attribute("Base class for multi-adapters.")
+ Annotation = interface.Attribute("Base class for persistent annotations.")
GlobalUtility = interface.Attribute("Base class for global utilities.")
LocalUtility = interface.Attribute("Base class for local utilities.")
View = interface.Attribute("Base class for browser views.")
Modified: grok/trunk/src/grok/meta.py
===================================================================
--- grok/trunk/src/grok/meta.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/meta.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -9,10 +9,12 @@
from zope.security.checker import NamesChecker, defineChecker
from zope.security.permission import Permission
from zope.security.interfaces import IPermission
+from zope.annotation.interfaces import IAnnotations
+from zope.app.publisher.xmlrpc import MethodPublisher
from zope.app.container.interfaces import IContainer
-from zope.app.publisher.xmlrpc import MethodPublisher
from zope.app.container.interfaces import INameChooser
+from zope.app.container.contained import contained
import grok
from grok import util, components, formlib
@@ -44,7 +46,7 @@
component.provideAdapter(factory, adapts=(adapter_context,),
provides=provides,
name=name)
-
+
class MultiAdapterGrokker(grok.ClassGrokker):
component_class = grok.MultiAdapter
@@ -414,3 +416,39 @@
for permission in permissions:
# TODO permission title and description
component.provideUtility(Permission(permission), name=permission)
+
+class AnnotationGrokker(grok.ClassGrokker):
+ component_class = grok.Annotation
+
+ def register(self, context, name, factory, module_info, templates):
+ adapter_context = util.determine_class_context(factory, context)
+ provides = util.class_annotation(factory, 'grok.provides', None)
+ if provides is None:
+ base_interfaces = interface.implementedBy(grok.Annotation)
+ factory_interfaces = interface.implementedBy(factory)
+ real_interfaces = list(factory_interfaces - base_interfaces)
+ util.check_implements_one_from_list(real_interfaces, factory)
+ provides = real_interfaces[0]
+
+ key = util.class_annotation(factory, 'grok.name', None)
+ if key is None:
+ key = factory.__module__ + '.' + factory.__name__
+
+ @component.adapter(adapter_context)
+ @interface.implementer(provides)
+ def getAnnotation(context):
+ annotations = IAnnotations(context)
+ try:
+ result = annotations[key]
+ except KeyError:
+ result = factory()
+ annotations[key] = result
+
+ # Containment has to be set up late to allow containment
+ # proxies to be applied, if needed. This does not trigger
+ # an event and is idempotent if containment is set up
+ # already.
+ contained_result = contained(result, context, key)
+ return contained_result
+
+ component.provideAdapter(getAnnotation)
Copied: grok/trunk/src/grok/tests/annotation/__init__.py (from rev 72076, grok/trunk/src/grok/tests/__init__.py)
Added: grok/trunk/src/grok/tests/annotation/annotation.py
===================================================================
--- grok/trunk/src/grok/tests/annotation/annotation.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/tests/annotation/annotation.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -0,0 +1,49 @@
+"""
+ >>> grok.grok(__name__)
+ >>> from zope import component
+ >>> from zope.annotation.attribute import AttributeAnnotations
+ >>> component.provideAdapter(AttributeAnnotations)
+
+We can adapt a model to an annotation interface and obtain a
+persistent annotation storage:
+
+ >>> manfred = Mammoth()
+ >>> branding = IBranding(manfred)
+ >>> branding.addBrand('mine')
+ >>> branding.addBrand('yours')
+
+Regetting the adapter will yield the same annotation storage:
+
+ >>> brands = IBranding(manfred).getBrands()
+ >>> brands.sort()
+ >>> brands
+ ['mine', 'yours']
+
+"""
+
+import grok
+from zope import interface
+from BTrees.OOBTree import OOTreeSet
+
+class Mammoth(grok.Model):
+ pass
+
+class IBranding(interface.Interface):
+
+ def addBrand(brand):
+ """Brand an animal with ``brand``, a string."""
+
+ def getBrands():
+ """Return a list of brands."""
+
+class Branding(grok.Annotation):
+ grok.implements(IBranding)
+
+ def __init__(self):
+ self._brands = OOTreeSet()
+
+ def addBrand(self, brand):
+ self._brands.insert(brand)
+
+ def getBrands(self):
+ return list(self._brands)
Property changes on: grok/trunk/src/grok/tests/annotation/annotation.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: grok/trunk/src/grok/tests/annotation/implementsmany.py
===================================================================
--- grok/trunk/src/grok/tests/annotation/implementsmany.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/tests/annotation/implementsmany.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -0,0 +1,27 @@
+"""
+An annotations class must implement either exactly one interface, or
+it should cspecify which of the many implemented interfaces it should
+be registered for. Ambiguities lead to errors:
+
+ >>> grok.grok(__name__)
+ Traceback (most recent call last):
+ GrokError: <class
+ 'grok.tests.annotation.implementsmany.MammothAnnotations'> is
+ implementing more than one interface (use grok.provides to specify
+ which one to use).
+"""
+
+import grok
+from zope import interface
+
+class Mammoth(grok.Model):
+ pass
+
+class IOneInterface(interface.Interface):
+ pass
+
+class IAnotherInterface(interface.Interface):
+ pass
+
+class MammothAnnotations(grok.Annotation):
+ grok.implements(IOneInterface, IAnotherInterface)
Property changes on: grok/trunk/src/grok/tests/annotation/implementsmany.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: grok/trunk/src/grok/tests/annotation/implementsnone.py
===================================================================
--- grok/trunk/src/grok/tests/annotation/implementsnone.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/tests/annotation/implementsnone.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -0,0 +1,20 @@
+"""
+Subclasses of grok.Annotation must implement at least one additional
+interface to indicate which annotation interface they provide and can
+be looked up with:
+
+ >>> grok.grok(__name__)
+ Traceback (most recent call last):
+ GrokError: <class 'grok.tests.annotation.implementsnone.Branding'>
+ must implement at least one interface (use grok.implements to
+ specify).
+
+"""
+
+import grok
+
+class Mammoth(grok.Model):
+ pass
+
+class Branding(grok.Annotation):
+ pass
Property changes on: grok/trunk/src/grok/tests/annotation/implementsnone.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: grok/trunk/src/grok/tests/annotation/name.py
===================================================================
--- grok/trunk/src/grok/tests/annotation/name.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/tests/annotation/name.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -0,0 +1,49 @@
+"""
+ >>> grok.grok(__name__)
+ >>> from zope import component
+ >>> from zope.annotation.attribute import AttributeAnnotations
+ >>> component.provideAdapter(AttributeAnnotations)
+
+If an annotation class doesn't specify anything else, its dotted name
+will be used as an annotation key:
+
+ >>> manfred = Mammoth()
+ >>> ann = IImplicitName(manfred)
+
+ >>> from zope.annotation.interfaces import IAnnotations
+ >>> 'grok.tests.annotation.name.ImplicitName' in IAnnotations(manfred)
+ True
+
+Of course, annotation classes can explicity specify the name of the
+annotation key that they will be stored under. That's useful if you
+want a meaningful key that's accessible from other applications and if
+you want to be able to move the class around during refactorings (then
+the dotted name will obviously change)
+
+ >>> ann = IExplicitName(manfred)
+ >>> 'grok.tests.annotation.name.ExplicitName' in IAnnotations(manfred)
+ False
+ >>> 'mammoth.branding' in IAnnotations(manfred)
+ True
+
+"""
+
+import grok
+from zope import interface
+from BTrees.OOBTree import OOTreeSet
+
+class Mammoth(grok.Model):
+ pass
+
+class IExplicitName(interface.Interface):
+ pass
+
+class IImplicitName(interface.Interface):
+ pass
+
+class ExplicitName(grok.Annotation):
+ grok.implements(IExplicitName)
+ grok.name('mammoth.branding')
+
+class ImplicitName(grok.Annotation):
+ grok.implements(IImplicitName)
Property changes on: grok/trunk/src/grok/tests/annotation/name.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: grok/trunk/src/grok/tests/annotation/provides.py
===================================================================
--- grok/trunk/src/grok/tests/annotation/provides.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/tests/annotation/provides.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -0,0 +1,37 @@
+"""
+ >>> grok.grok(__name__)
+ >>> from zope import component
+ >>> from zope.annotation.attribute import AttributeAnnotations
+ >>> component.provideAdapter(AttributeAnnotations)
+
+If an annotation class implements more than one interface, it has to
+declare which one it should be registered for using ``grok.provides``.
+
+ >>> manfred = Mammoth()
+ >>> ann = IOneInterface(manfred)
+
+It can then be looked up only using that one interface:
+
+ >>> IAnotherOne(manfred)
+ Traceback (most recent call last):
+ TypeError: ('Could not adapt', <grok.tests.annotation.provides.Mammoth object at ...>, <InterfaceClass grok.tests.annotation.provides.IAnotherOne>)
+
+
+"""
+
+import grok
+from zope import interface
+from BTrees.OOBTree import OOTreeSet
+
+class Mammoth(grok.Model):
+ pass
+
+class IOneInterface(interface.Interface):
+ pass
+
+class IAnotherOne(interface.Interface):
+ pass
+
+class MammothAnnotation(grok.Annotation):
+ grok.implements(IOneInterface, IAnotherOne)
+ grok.provides(IOneInterface)
Property changes on: grok/trunk/src/grok/tests/annotation/provides.py
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py 2007-01-19 21:58:52 UTC (rev 72110)
+++ grok/trunk/src/grok/tests/test_grok.py 2007-01-19 23:35:02 UTC (rev 72111)
@@ -35,7 +35,7 @@
for name in ['adapter', 'error', 'view', 'scan', 'event', 'security',
'zcml', 'static', 'utility', 'xmlrpc', 'container',
'traversal', 'form', 'site', 'grokker', 'directive', 'util',
- 'baseclass']:
+ 'baseclass', 'annotation']:
suite.addTest(suiteFromPackage(name))
return suite
More information about the Checkins
mailing list