Skip to content

Commit

Permalink
Merge pull request #58 from zopefoundation/issue9
Browse files Browse the repository at this point in the history
Fix the subscriber directive when a factory is given that implements an interface and no provides= attribute is specified.
  • Loading branch information
jamadden committed Mar 18, 2021
2 parents 5fd805d + f81a7f8 commit 7e86836
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -19,6 +19,12 @@

- Add support for Python 3.9

- Fix the ``<subscriber>`` ZCML directive to allow a missing
``provides=`` attribute when a ``factory=`` is given and the Python
object has been decorated with ``@implementer`` and implements a
single interface. This has been documented, but hasn't worked
before. See `issue 9
<https://github.com/zopefoundation/zope.component/issues/9>`_.

4.6.2 (2020-07-03)
==================
Expand Down
3 changes: 1 addition & 2 deletions docs/zcml.rst
Expand Up @@ -690,13 +690,12 @@ subscriber should be registered for:
>>> clearZCML()
>>> runSnippet('''
... <subscriber
... provides="zope.component.testfiles.adapter.IS"
... factory="zope.component.testfiles.adapter.A3"
... />''')

>>> content = Content()
>>> a2 = A2()
>>> subscribers = zope.component.subscribers((content, a1, a2), IS)
>>> subscribers = zope.component.subscribers((content, a1, a2), I3)

>>> a3 = subscribers[0]
>>> a3.__class__ is A3
Expand Down
23 changes: 23 additions & 0 deletions src/zope/component/tests/test_zcml.py
Expand Up @@ -598,6 +598,29 @@ class Foo(object):
self.assertEqual(action['discriminator'], None)
self.assertEqual(action['args'], ('', Interface))

def test_no_for__no_provides_subscriber_adapts_subscriber_implements(self):
from zope.interface import Interface
from zope.interface import implementer
from zope.component._declaration import adapter
from zope.component.zcml import handler
class IFoo(Interface):
pass
@adapter(Interface)
@implementer(IFoo)
class _Factory(object):
def __init__(self, context):
self.context = context
_cfg_ctx = _makeConfigContext()
self._callFUT(_cfg_ctx, factory=_Factory)
self.assertEqual(len(_cfg_ctx._actions), 3)
self.assertEqual(_cfg_ctx._actions[0][0], ())
# Register the subscriber
action = _cfg_ctx._actions[0][1]
self.assertEqual(action['callable'], handler)
self.assertIsNone(action['discriminator'])
self.assertEqual(action['args'],
('registerSubscriptionAdapter', _Factory, (Interface,), IFoo,
'', 'TESTING'))

class Test_utility(unittest.TestCase):

Expand Down
4 changes: 4 additions & 0 deletions src/zope/component/zcml.py
Expand Up @@ -303,6 +303,10 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None,
else:
if handler is not None:
raise TypeError("Cannot use handler with factory")
if provides is None:
p = list(implementedBy(factory))
if len(p) == 1:
provides = p[0]
if provides is None:
raise TypeError(
"You must specify a provided interface when registering "
Expand Down

0 comments on commit 7e86836

Please sign in to comment.