[ZODB-Dev] Persistent-derived class instances always callable

Tim Peters tim at zope.com
Fri Oct 17 17:01:56 EDT 2003


[Shane Hathaway]
> ExtensionClass.Base, a C type, fills the tp_call slot.  callable()
> looks to see if tp_call is non-null and considers this check to be
> authoritative.

Well, it is authoritative:  that tp_call is set means the thing is in fact
callable from Python's POV:  the C function tp_call points to is in fact
called.  In this case, the function it points to raises an AttributeError
about "__call__" not being found.  Python can't guess that this will be the
result of calling tp_call, it can only tell you that it found something *to*
call -- that the attempt to call isn't necessarily futile.  As always, an
inquiry function in Python can at best tell you whether it's possible for
Python to *try* to do a thing; it can't tell you whether the actual attempt
to try it will succeed, or how it may fail.  The only way to determine the
latter is to try it.

It's analogous to this:

>>> class C:
        def __call__(self):
            raise AttributeError, "__call__"


>>> c = C()
>>> callable(c)
True
>>> hasattr(c, "__call__")
True
>>> c()
Traceback (most recent call last):
  ...
AttributeError: __call__
>>>



> Although some ExtensionClass instances are callable and some are not,

If tp_call is set, they are callable.

> callable() doesn't go to the effort of discerning the difference, so it
> always returns True for ExtensionClass instances.

If that's undesirable, it shouldn't set tp_call all the time <wink>.

> I think that callable() should only return True if the object has a
> __call__ attribute.  Now that even functions and methods have __call__
> attributes (Python 2.2+), simply checking for the presence of __call__
> is actually more reliable than the current callable() implementation.

Going back to John's example:

>>> import ZODB
>>> from Persistence import Persistent
>>> class Foo(Persistent): pass

>>> callable(Foo)
True
>>> Foo()  # and it really is callable, by anybody's definition
<Foo instance at 00F95590>
>>> hasattr(Foo, "__call__")  # but it doesn't have "__call__"
False
>>>




More information about the ZODB-Dev mailing list