[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