zope.interface icon indicating copy to clipboard operation
zope.interface copied to clipboard

verifyObject doesn't correctly work on classes with classProvides

Open tseaver opened this issue 9 years ago • 1 comments

In https://bugs.launchpad.net/zope.interface/+bug/675424, Christian Zagrodnik reported:

When for instance declaring a factory classProvides is handy at times:

>>> import zope.interface
>>> import zope.interface.verify
>>>
>>> class IFoo(zope.interface.Interface):
...     def __call__(arg):
...         pass
...
>>> class FooOkay(object):
...     zope.interface.classProvides(IFoo)
...     def __init__(self, arg):
...         pass
...
>>> zope.interface.verify.verifyObject(IFoo, FooOkay)
True

That's was working.

The next case should raise an error, but doesn't. IFoo requires an arg to be passed which the following class doesn't implement:

>>> class FooVerifiedByNotLikeSpec(object):
...     zope.interface.classProvides(IFoo)
...
>>> zope.interface.verify.verifyObject(IFoo, FooVerifiedByNotLikeSpec)
True

The next case should actually pass but doesn't.

>>> class FooBroken(object):
...     zope.interface.classProvides(IFoo)
...     def __init__(self, arg):
...         pass
...     def __call__(self):
...         pass
...
>>> zope.interface.verify.verifyObject(IFoo, FooBroken)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/zagy/development/eggs/zope.interface-3.6.1-py2.7-macosx-10.6-x86_64.egg/zope/interface/verify.py", line 102, in verifyObject
    return _verify(iface, candidate, tentative, vtype='o')
File "/Users/zagy/development/eggs/zope.interface-3.6.1-py2.7-macosx-10.6-x86_64.egg/zope/interface/verify.py", line 94, in _verify
    raise BrokenMethodImplementation(name, mess)
BrokenMethodImplementation: The implementation of __call__ violates its contract
        because implementation doesn't allow enough arguments.

Apparently verifyObject doesn't take any special care for classes which proivde an interface.

tseaver avatar Jan 12 '15 00:01 tseaver

This is not a trivial problem. What's happening is that verification doesn't take special method lookup into account. How should interfaces handle special methods, like __add__ and __contains__? These methods can never be directly provided, really, (except for modules, which as of Python 3.7 can directly provide __dir__ and __getattr__ functions).

And for classes without a more specific metaclass than type, it could be argued that they could only ever "directly provide" __call__(*args, **kwargs), never something more specific. That the type.__call__() method used for ClassObj() invokes __new__ and, optionally, __new__, is an implementation detail that verifyObject can't be expected to handle.

mjpieters avatar Feb 28 '18 08:02 mjpieters