[Checkins] SVN: grokcore.component/trunk/ * The grokkers for adapters and global utilities did not use the

Philipp von Weitershausen philikon at philikon.de
Fri May 2 05:43:17 EDT 2008


Log message for revision 86061:
  * The grokkers for adapters and global utilities did not use the
    correct value for the *provided* interface in the configuration
    action discriminator.  Because of this, uninformative and
    potentially wrong conflict errors would occur, as well as no
    conflict where a conflict should have occurred.
  
  * The grokker for the ``global_utility()`` directive did immediate
    registrations instead of generating configuration actions.
    Therefore it did not provoke ``ConflictErrors`` for conflicting
    registrations.
  
  

Changed:
  U   grokcore.component/trunk/CHANGES.txt
  U   grokcore.component/trunk/README.txt
  U   grokcore.component/trunk/src/grokcore/component/meta.py
  A   grokcore.component/trunk/src/grokcore/component/tests/adapter/conflict.py
  A   grokcore.component/trunk/src/grokcore/component/tests/utility/conflict.py
  A   grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany.py
  A   grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany2.py
  A   grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone.py
  A   grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone2.py
  U   grokcore.component/trunk/src/grokcore/component/util.py

-=-
Modified: grokcore.component/trunk/CHANGES.txt
===================================================================
--- grokcore.component/trunk/CHANGES.txt	2008-05-02 09:38:54 UTC (rev 86060)
+++ grokcore.component/trunk/CHANGES.txt	2008-05-02 09:43:17 UTC (rev 86061)
@@ -4,8 +4,17 @@
 1.1 (unreleased)
 ----------------
 
-* ...
+* The grokkers for adapters and global utilities did not use the
+  correct value for the *provided* interface in the configuration
+  action discriminator.  Because of this, uninformative and
+  potentially wrong conflict errors would occur, as well as no
+  conflict where a conflict should have occurred.
 
+* The grokker for the ``global_utility()`` directive did immediate
+  registrations instead of generating configuration actions.
+  Therefore it did not provoke ``ConflictErrors`` for conflicting
+  registrations.
+
 * Improved documentation
 
 1.0 (2008-05-01)

Modified: grokcore.component/trunk/README.txt
===================================================================
--- grokcore.component/trunk/README.txt	2008-05-02 09:38:54 UTC (rev 86060)
+++ grokcore.component/trunk/README.txt	2008-05-02 09:43:17 UTC (rev 86061)
@@ -52,6 +52,9 @@
 Examples
 ========
 
+Adapter
+-------
+
 Here's a simple adapter that may be useful in Zope.  It extracts the
 languages that a user prefers from the request::
 
@@ -76,6 +79,35 @@
           # According to IUserPreferredLanguages, we must return a list.
           return [lang]
 
+Multi-adapter
+-------------
+
+Here's a multi-adapter that functions as a content provider as known
+from the ``zope.contentprovider`` library.  Content providers are
+components that return snippets of HTML.  They're multi-adapters for
+the content object (model), the request and the view that they're
+supposed to be a part of::
+
+  import grokcore.component
+  from zope.publisher.interfaces.browser import IBrowserRequest
+  from zope.publisher.interfaces.browser import IBrowserPage
+  from zope.contentprovider.interfaces import IContentProvider
+
+  class HelloWorldProvider(grokcore.component.MultiAdapter):
+      """Display Hello World!"""
+      grokcore.component.adapts(Interface, IBrowserRequest, IBrowserPage)
+      grokcore.component.implements(IContentProvider)
+
+      def update(self):
+          pass
+
+      def render(self):
+          return u'<p>Hello World!</p>'
+
+
+Global utility
+--------------
+
 Here's a simple named utility, again from the Zope world.  It's a
 translation domain.  In other words, it contains translations of user
 messages and is invoked when the i18n machinery needs to translate

Modified: grokcore.component/trunk/src/grokcore/component/meta.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/meta.py	2008-05-02 09:38:54 UTC (rev 86060)
+++ grokcore.component/trunk/src/grokcore/component/meta.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -17,12 +17,14 @@
 import grokcore.component
 import zope.component.interface
 
-from zope import component
+from zope import component, interface
 from martian import util
+from martian.error import GrokError
 from grokcore.component.util import check_adapts
 from grokcore.component.util import check_module_component
 from grokcore.component.util import determine_module_component
 from grokcore.component.util import determine_class_component
+from grokcore.component.util import check_provides_one
 
 def get_context(module_info, factory):
     return determine_class_component(module_info, factory,
@@ -43,6 +45,7 @@
     provides = util.class_annotation(factory, 'grok.provides', None)
     if provides is None:
         util.check_implements_one(factory)
+        provides = list(interface.implementedBy(factory))[0]
     return provides
 
 
@@ -99,17 +102,24 @@
     priority = 1100
 
     def grok(self, name, factory, module_info, config, **kw):
-        provides = get_provides(factory)
+        provides = util.class_annotation(factory, 'grok.provides', None)
+        direct = util.class_annotation(factory, 'grok.direct', False)
         name = get_name(factory)
 
-        direct = util.class_annotation(factory, 'grok.direct', False)
-        if not direct:
-            factory = factory()
+        if direct:
+            obj = factory
+            if provides is None:
+                check_provides_one(factory)
+                provides = list(interface.providedBy(factory))[0]
+        else:
+            obj = factory()
+            if provides is None:
+                provides = get_provides(factory)
 
         config.action(
             discriminator=('utility', provides, name),
             callable=component.provideUtility,
-            args=(factory, provides, name),
+            args=(obj, provides, name),
             )
         return True
 
@@ -142,15 +152,24 @@
         infos = module_info.getAnnotation('grok.global_utility', [])
 
         for info in infos:
-            if info.provides is None:
-                util.check_implements_one(info.factory)
+            provides = info.provides
+
             if info.direct:
                 obj = info.factory
+                if provides is None:
+                    check_provides_one(obj)
+                    provides = list(interface.providedBy(obj))[0]
             else:
                 obj = info.factory()
-            component.provideUtility(obj,
-                                     provides=info.provides,
-                                     name=info.name)
+                if provides is None:
+                    provides = get_provides(info.factory)
+
+            config.action(
+                discriminator=('utility', provides, info.name),
+                callable=component.provideUtility,
+                args=(obj, provides, info.name),
+                )
+
         return True
 
 

Added: grokcore.component/trunk/src/grokcore/component/tests/adapter/conflict.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/adapter/conflict.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/adapter/conflict.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -0,0 +1,39 @@
+"""
+Registering two adapters for the same target interface should provoke
+a conflict, even if the interface is guessed (instead of being
+explicitly declared with grok.provides):
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+  ...
+  ConfigurationConflictError: Conflicting configuration actions
+    For: ('adapter', <InterfaceClass grokcore.component.tests.adapter.conflict.ICave>, <InterfaceClass grokcore.component.tests.adapter.conflict.IDecoration>, '')
+
+"""
+import grokcore.component as grok
+from zope.interface import Interface
+
+class ICave(Interface):
+    pass
+
+class IDecoration(Interface):
+    pass
+
+class ICaveCleaning(Interface):
+    pass
+
+class Cave(object):
+    grok.implements(ICave)
+
+
+class ImplicitProvides(grok.Adapter):
+    """Here the provided interface is guessed because the class only
+    implements one interface."""
+    grok.context(ICave)
+    grok.implements(IDecoration)
+
+class ExplicitProvides(grok.Adapter):
+    """Here the provided interface is specific explicitly."""
+    grok.context(ICave)
+    grok.implements(IDecoration, ICaveCleaning)
+    grok.provides(IDecoration)


Property changes on: grokcore.component/trunk/src/grokcore/component/tests/adapter/conflict.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grokcore.component/trunk/src/grokcore/component/tests/utility/conflict.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/utility/conflict.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/utility/conflict.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -0,0 +1,89 @@
+"""
+Trying to register two utilities for the same interface (and
+potentially under the same name) will generate a configuration
+conflict:
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+  ...
+  ConfigurationConflictError: Conflicting configuration actions
+    For: ('utility', <InterfaceClass grokcore.component.tests.utility.conflict.IUtilityInterface>, 'class and module')
+  <BLANKLINE>
+  <BLANKLINE>
+    For: ('utility', <InterfaceClass grokcore.component.tests.utility.conflict.IUtilityInterface>, 'direct class')
+  <BLANKLINE>
+  <BLANKLINE>
+    For: ('utility', <InterfaceClass grokcore.component.tests.utility.conflict.IUtilityInterface>, 'explicit class')
+  <BLANKLINE>
+  <BLANKLINE>
+    For: ('utility', <InterfaceClass grokcore.component.tests.utility.conflict.IUtilityInterface>, 'implicit class')
+  <BLANKLINE>
+  <BLANKLINE>
+    For: ('utility', <InterfaceClass grokcore.component.tests.utility.conflict.IUtilityInterface>, 'mixed class')
+  <BLANKLINE>
+  <BLANKLINE>
+
+"""
+import grokcore.component as grok
+from zope.interface import Interface, classProvides
+
+class IUtilityInterface(Interface):
+    pass
+
+class IAnotherInterface(Interface):
+    pass
+
+
+class Implicit1(grok.GlobalUtility):
+    grok.implements(IUtilityInterface)
+    grok.name('implicit class')
+
+class Implicit2(grok.GlobalUtility):
+    grok.implements(IUtilityInterface)
+    grok.name('implicit class')
+
+
+class Explicit1(grok.GlobalUtility):
+    grok.implements(IUtilityInterface, IAnotherInterface)
+    grok.provides(IUtilityInterface)
+    grok.name('explicit class')
+
+class Explicit2(grok.GlobalUtility):
+    grok.implements(IUtilityInterface, IAnotherInterface)
+    grok.provides(IUtilityInterface)
+    grok.name('explicit class')
+
+
+class Mixed1(grok.GlobalUtility):
+    grok.implements(IUtilityInterface, IAnotherInterface)
+    grok.provides(IUtilityInterface)
+    grok.name('mixed class')
+
+class Mixed2(grok.GlobalUtility):
+    grok.implements(IUtilityInterface)
+    grok.name('mixed class')
+
+
+class Direct1(grok.GlobalUtility):
+    classProvides(IUtilityInterface)
+    grok.name('direct class')
+    grok.direct()
+
+class Direct2(grok.GlobalUtility):
+    classProvides(IUtilityInterface)
+    grok.name('direct class')
+    grok.direct()
+
+
+class ClassLevel(grok.GlobalUtility):
+    """This utility inherits from Grok's base class and is registered
+    this way."""
+    grok.implements(IUtilityInterface)
+    grok.name('class and module')
+
+class ModuleLevel(object):
+    """This utility doesn't inherit from Grok's base class and is
+    registered explicitly using the module-level directive below."""
+    grok.implements(IUtilityInterface)
+
+grok.global_utility(ModuleLevel, name='class and module')


Property changes on: grokcore.component/trunk/src/grokcore/component/tests/utility/conflict.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -0,0 +1,24 @@
+"""
+Subclasses of grok.GlobalUtility that are supposed to be registered
+directly as utilities and which provide more than one interface must
+specify which interface to use for the registration:
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grokcore.component.tests.utility.providesmany.Club'>
+  provides more than one interface (use grok.provides to specify which one
+  to use).
+"""
+import grokcore.component as grok
+from zope import interface
+
+class IClub(interface.Interface):
+    pass
+
+class ISpikyClub(interface.Interface):
+    pass
+
+class Club(grok.GlobalUtility):
+    interface.classProvides(IClub, ISpikyClub)
+    grok.direct()


Property changes on: grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany2.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany2.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany2.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -0,0 +1,25 @@
+"""
+Subclasses of grok.GlobalUtility that are supposed to be registered
+directly as utilities and which provide more than one interface must
+specify which interface to use for the registration:
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grokcore.component.tests.utility.providesmany2.Club'>
+  provides more than one interface (use grok.provides to specify which one
+  to use).
+"""
+import grokcore.component as grok
+from zope import interface
+
+class IClub(interface.Interface):
+    pass
+
+class ISpikyClub(interface.Interface):
+    pass
+
+class Club(object):
+    interface.classProvides(IClub, ISpikyClub)
+
+grok.global_utility(Club, direct=True)


Property changes on: grokcore.component/trunk/src/grokcore/component/tests/utility/providesmany2.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -0,0 +1,14 @@
+"""
+Subclasses of grok.GlobalUtility must implement exactly one interface:
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grokcore.component.tests.utility.providesnone.Club'>
+  must provide at least one interface (use zope.interface.classProvides
+  to specify).
+"""
+import grokcore.component as grok
+
+class Club(grok.GlobalUtility):
+    grok.direct()


Property changes on: grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone2.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone2.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone2.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -0,0 +1,16 @@
+"""
+Subclasses of grok.GlobalUtility must implement exactly one interface:
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grokcore.component.tests.utility.providesnone2.Club'>
+  must provide at least one interface (use zope.interface.classProvides
+  to specify).
+"""
+import grokcore.component as grok
+
+class Club(object):
+    pass
+
+grok.global_utility(Club, direct=True)


Property changes on: grokcore.component/trunk/src/grokcore/component/tests/utility/providesnone2.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: grokcore.component/trunk/src/grokcore/component/util.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/util.py	2008-05-02 09:38:54 UTC (rev 86060)
+++ grokcore.component/trunk/src/grokcore/component/util.py	2008-05-02 09:43:17 UTC (rev 86061)
@@ -14,7 +14,7 @@
 """Grok utility functions.
 """
 
-from zope import component
+from zope import component, interface
 
 from martian.error import GrokError
 from martian.util import class_annotation, methods_from_class, scan_for_classes
@@ -114,3 +114,14 @@
     check_module_component(class_, component,
                            component_name, component_directive)
     return component
+
+def check_provides_one(obj):
+    provides = list(interface.providedBy(obj))
+    if len(provides) < 1:
+        raise GrokError("%r must provide at least one interface "
+                        "(use zope.interface.classProvides to specify)."
+                        % obj, obj)
+    if len(provides) > 1:
+        raise GrokError("%r provides more than one interface "
+                        "(use grok.provides to specify which one to use)."
+                        % obj, obj)



More information about the Checkins mailing list