Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

verifyClass(IAdapterRegistry, AdapterRegistry)... #208

Closed
liveFreeOrCode opened this issue Apr 22, 2020 · 1 comment · Fixed by #209
Closed

verifyClass(IAdapterRegistry, AdapterRegistry)... #208

liveFreeOrCode opened this issue Apr 22, 2020 · 1 comment · Fixed by #209

Comments

@liveFreeOrCode
Copy link

liveFreeOrCode commented Apr 22, 2020

from zope.interface.adapter import AdapterRegistry, IAdapterRegistry

verifyClass(IAdapterRegistry, AdapterRegistry)
...
MultipleInvalid: The object <class 'zope.interface.adapter.AdapterRegistry'> has failed to implement interface <InterfaceClass zope.interface.interfaces.IAdapterRegistry>:
    The zope.interface.interfaces.IAdapterRegistry.lookup(required, provided, name='', default=None) attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.queryMultiAdapter(objects, provided, name='', default=None) attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.lookup1(required, provided, name='', default=None) attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.queryAdapter(object, provided, name='', default=None) attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.adapter_hook(provided, object, name='', default=None) attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.lookupAll(required, provided) attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.names(required, provided) attribute was not provided
    The contract of zope.interface.interfaces.IAdapterRegistry.subscribe(required, provided, subscriber, name='') is violated because 'BaseAdapterRegistry.subscribe(self, required, provided, value)' doesn't allow enough arguments
    The zope.interface.interfaces.IAdapterRegistry.subscriptions(required, provided, name='') attribute was not provided
    The zope.interface.interfaces.IAdapterRegistry.subscribers(objects, provided, name='') attribute was not provided

Is there something I'm missing?

@jamadden
Copy link
Member

jamadden commented Apr 22, 2020

verifyClass can only check whether a class statically meets the requirements of an interface. AdapterRegistry does not statically meet the requirements of IAdapterRegistry, and verifyClass is correct to point that out. For example:

>>> from zope.interface.adapter import AdapterRegistry
>>> AdapterRegistry.lookup
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'AdapterRegistry' has no attribute 'lookup'

This is because many attributes are delegated to a different object at runtime; that's part of the difference between implements and provides.

def _createLookup(self):
self._v_lookup = self.LookupClass(self)
for name in self._delegated:
self.__dict__[name] = getattr(self._v_lookup, name)

(#15 is somewhat similar).

Given the dynamic nature of python, and the distinction between implements and provides verifyObject is a better fit here.

However, even doing that does appear to reveal a discrepancy:

>>> from zope.interface import *
>>> from zope.interface.adapter import *
>>> from zope.interface.verify import *
>>> from zope.interface.interfaces import IAdapterRegistry
>>> verifyObject(IAdapterRegistry, AdapterRegistry())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "//zope.interface/src/zope/interface/verify.py", line 172, in verifyObject
    return _verify(iface, candidate, tentative, vtype='o')
  File "//zope.interface/src/zope/interface/verify.py", line 92, in _verify
    raise MultipleInvalid(iface, candidate, excs)
zope.interface.exceptions.MultipleInvalid: The object <zope.interface.adapter.AdapterRegistry object at 0x10e8fd2d0> has failed to implement interface <InterfaceClass zope.interface.interfaces.IAdapterRegistry>:
    The contract of zope.interface.interfaces.IAdapterRegistry.subscribe(required, provided, subscriber, name='') is violated because 'BaseAdapterRegistry.subscribe(required, provided, value)' doesn't allow enough arguments
    The contract of zope.interface.interfaces.IAdapterRegistry.subscribers(objects, provided, name='') is violated because 'AdapterLookupBase.subscribers(objects, provided)' doesn't allow enough arguments

I don't know if this is due to a limitation in C extension methods, or to a problem in the interface definition. On a quick examination, it appears to be a problem with the interface.

>>> reg = AdapterRegistry()
>>> reg.subscribe
<bound method BaseAdapterRegistry.subscribe of <zope.interface.adapter.AdapterRegistry object at 0x10e91f960>>
>>> help(reg.subscribe)
Help on method subscribe in module zope.interface.adapter:

subscribe(required, provided, value) method of zope.interface.adapter.AdapterRegistry instance

None
>>> reg.subscribe('req', 'prov', 'val', 'name')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: subscribe() takes 4 positional arguments but 5 were given
>>> reg.subscribe('req', 'prov', 'val', name='name')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: subscribe() got an unexpected keyword argument 'name'

jamadden added a commit that referenced this issue Apr 23, 2020
…and subscriptions.

They were defined to accept a name argument, but the actual implementation doesn't. Add tests for this. Fixes #208.

Also in test_adapter.py modernize idioms from assertTrue(x in y) and assertTrue(x is y) to assertIn and assertIs.
jamadden added a commit that referenced this issue Apr 23, 2020
…and subscriptions.

They were defined to accept a name argument, but the actual implementation doesn't. Add tests for this. Fixes #208.

Also in test_adapter.py modernize idioms from assertTrue(x in y) and assertTrue(x is y) to assertIn and assertIs.
netbsd-srcmastr referenced this issue in NetBSD/pkgsrc Oct 18, 2020
5.1.2 (2020-10-01)
==================

- Make sure to call each invariant only once when validating invariants.
  Previously, invariants could be called multiple times because when an
  invariant is defined in an interface, it's found by in all interfaces
  inheriting from that interface.  See `pull request 215
  <https://github.com/zopefoundation/zope.interface/pull/215/>`_.

5.1.1 (2020-09-30)
==================

- Fix the method definitions of ``IAdapterRegistry.subscribe``,
  ``subscriptions`` and ``subscribers``. Previously, they all were
  defined to accept a ``name`` keyword argument, but subscribers have
  no names and the implementation of that interface did not accept
  that argument. See `issue 208
  <https://github.com/zopefoundation/zope.interface/issues/208>`_.

- Fix a potential reference leak in the C optimizations. Previously,
  applications that dynamically created unique ``Specification``
  objects (e.g., used ``@implementer`` on dynamic classes) could
  notice a growth of small objects over time leading to increased
  garbage collection times. See `issue 216
  <https://github.com/zopefoundation/zope.interface/issues/216>`_.

  .. caution::

     This leak could prevent interfaces used as the bases of
     other interfaces from being garbage collected. Those interfaces
     will now be collected.

     One way in which this would manifest was that ``weakref.ref``
     objects (and things built upon them, like
     ``Weak[Key|Value]Dictionary``) would continue to have access to
     the original object even if there were no other visible
     references to Python and the original object *should* have been
     collected. This could be especially problematic for the
     ``WeakKeyDictionary`` when combined with dynamic or local
     (created in the scope of a function) interfaces, since interfaces
     are hashed based just on their name and module name. See the
     linked issue for an example of a resulting ``KeyError``.

     Note that such potential errors are not new, they are just once
     again a possibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants