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

verifyObject doesn't verify keyword arguments

Open jluttine opened this issue 5 years ago • 4 comments

Given the following:

class IFoo(zope.interface.Interface):
    def bar(lorem="ipsum", alice=42):
        """ ... """

@zope.interface.implementer(IFoo)
class Foo():
    def bar(self, x=666, y=99):
        return x + y

I would have expected verification to fail but it doesn't:

>>> zope.interface.verify.verifyObject(IFoo, Foo())
True

How can I force verifyObject to verify the function signature exactly? So that it it is exactly the same (same positional and keyword arguments with same names in same order and also the same *args and **kwargs).

jluttine avatar Jun 14 '19 15:06 jluttine

Jaakko Luttinen wrote at 2019-6-14 08:06 -0700:

Given the following:

class IFoo(zope.interface.Interface):
   def bar(lorem="ipsum", alice=42):
       """ ... """

@zope.interface.implementer(IFoo)
class Foo():
   def bar(self, x=666, y=99):
       return x + y

I would have expected verification to fail but it doesn't:

>>> zope.interface.verify.verifyObject(IFoo, Foo())
True

How can I force verifyObject to verify the function signature exactly? So that it it is exactly the same (same positional and keyword arguments with same names in same order and also the same *args and **kwargs).

A paramter definition of the form param=value can mean two things:

  1. param is a positional parameter with default value value
  2. param is a keyword argument

For case 1, the parameter name has no importance (as positional parameters are identified via position, not via name).

zope.interface cannot separate those two cases -- Python has only learned recently (Python 3+) to distinguish between those cases and zope.interface predates this. Because zope.interface cannot distinguish the cases, it is permissive (i.e. does not check the names).

You will need your own verification in order to get the names checked.

To help with its definition, you could make use of dm.reuse.rebindFunction (you find dm.reuse on PyPI): it allows you to reuse a function definition (_verify, verifyClass and verifyObject, all from zope.interface.verify, in this case) and change some of its "globals" (in your case _incompat and _verify). In your version of _incompat you would check in addition the parameter names.

For a clean solution, zope.interface would need to learn how to differentiate between a positional argument with default value and a true keyword argument and check the names for the latter.

d-maurer avatar Jun 14 '19 17:06 d-maurer

See #63 for some details. Maybe at least some parts of the issue can be fixed via a PR.

icemac avatar Jun 21 '19 07:06 icemac

See #63 for some details. Maybe at least some parts of the issue can be fixed via a PR.

Will zope.interface drop Python 2 support soon? After there is only Python 3, zope.interface should (of course) support Python 3's new keyword only arguments (and for them check that they do not change names).

I am against checking the names of positional arguments - whether with or without toggle: a positional argument is defined by its position not by its name. If I want an argument defined by name, I use a keyword only argument (provided I can do this because I support only Python 3).

d-maurer avatar Jun 21 '19 08:06 d-maurer

From 2020-01-01 on there is no need to support Python 2.7 any more. (People needing Python 2 support can still use older package versions.)

icemac avatar Jun 25 '19 06:06 icemac