Skip to content

Commit

Permalink
Backport from grokcore.component 1.0.1:
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
philikon committed May 2, 2008
1 parent 5bdb81f commit ca0e5ef
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 11 deletions.
11 changes: 11 additions & 0 deletions CHANGES.txt
Expand Up @@ -7,6 +7,17 @@ Grok changes
Bug fixes
---------

* 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.

* Removed quickly hacked testsetup code from grok.testing.

* Port fix of zope.formlib to correctly adapt the context to a FormField's
Expand Down
38 changes: 28 additions & 10 deletions src/grok/meta.py
Expand Up @@ -55,6 +55,7 @@
from grok.util import check_module_component, determine_module_component
from grok.util import determine_class_component
from grok.util import determine_class_directive, public_methods_from_class
from grok.util import check_provides_one
from grok.rest import RestPublisher
from grok.interfaces import IRESTSkinType

Expand All @@ -76,6 +77,7 @@ def get_provides(factory):
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

class ContextGrokker(martian.GlobalGrokker):
Expand Down Expand Up @@ -141,17 +143,24 @@ class GlobalUtilityGrokker(martian.ClassGrokker):
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

Expand Down Expand Up @@ -555,15 +564,24 @@ def grok(self, name, module, module_info, config, **kw):
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


Expand Down
39 changes: 39 additions & 0 deletions src/grok/tests/adapter/conflict.py
@@ -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 grok.tests.adapter.conflict.ICave>, <InterfaceClass grok.tests.adapter.conflict.IDecoration>, '')
"""
import 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)
89 changes: 89 additions & 0 deletions src/grok/tests/utility/conflict.py
@@ -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 grok.tests.utility.conflict.IUtilityInterface>, 'class and module')
<BLANKLINE>
<BLANKLINE>
For: ('utility', <InterfaceClass grok.tests.utility.conflict.IUtilityInterface>, 'direct class')
<BLANKLINE>
<BLANKLINE>
For: ('utility', <InterfaceClass grok.tests.utility.conflict.IUtilityInterface>, 'explicit class')
<BLANKLINE>
<BLANKLINE>
For: ('utility', <InterfaceClass grok.tests.utility.conflict.IUtilityInterface>, 'implicit class')
<BLANKLINE>
<BLANKLINE>
For: ('utility', <InterfaceClass grok.tests.utility.conflict.IUtilityInterface>, 'mixed class')
<BLANKLINE>
<BLANKLINE>
"""
import 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')
24 changes: 24 additions & 0 deletions src/grok/tests/utility/providesmany.py
@@ -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 'grok.tests.utility.providesmany.Club'>
provides more than one interface (use grok.provides to specify which one
to use).
"""
import 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()
25 changes: 25 additions & 0 deletions src/grok/tests/utility/providesmany2.py
@@ -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 'grok.tests.utility.providesmany2.Club'>
provides more than one interface (use grok.provides to specify which one
to use).
"""
import 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)
14 changes: 14 additions & 0 deletions src/grok/tests/utility/providesnone.py
@@ -0,0 +1,14 @@
"""
Subclasses of grok.GlobalUtility must implement exactly one interface:
>>> grok.testing.grok(__name__)
Traceback (most recent call last):
...
GrokError: <class 'grok.tests.utility.providesnone.Club'>
must provide at least one interface (use zope.interface.classProvides
to specify).
"""
import grok

class Club(grok.GlobalUtility):
grok.direct()
16 changes: 16 additions & 0 deletions src/grok/tests/utility/providesnone2.py
@@ -0,0 +1,16 @@
"""
Subclasses of grok.GlobalUtility must implement exactly one interface:
>>> grok.testing.grok(__name__)
Traceback (most recent call last):
...
GrokError: <class 'grok.tests.utility.providesnone2.Club'>
must provide at least one interface (use zope.interface.classProvides
to specify).
"""
import grok

class Club(object):
pass

grok.global_utility(Club, direct=True)
13 changes: 12 additions & 1 deletion src/grok/util.py
Expand Up @@ -17,7 +17,7 @@
import urllib

import zope.location.location
from zope import component
from zope import component, interface
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.browser.absoluteurl import _safe as SAFE_URL_CHARACTERS

Expand Down Expand Up @@ -188,3 +188,14 @@ def determine_class_component(module_info, class_,
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)

0 comments on commit ca0e5ef

Please sign in to comment.