[Zope-dev] Re: "hasattr" geddon

Tim Peters tim.peters at gmail.com
Sat Jul 10 13:41:30 EDT 2004


[Shane Hathaway]
> Hmm.  I just heard about this "hasattr geddon".  hasattr is *good*.  Why are
> we fixing hasattr and bare excepts

Well, bare excepts are generally bad news in any application -- they
almost always turn out to catch more than the author intended
(including things like SystemExit and KeyboardInterrupt, which are
rarely intended to be swallowed).

> when the real problem is ZODB?  ZODB
> should *not* be sensitive to the way the application handles ConflictErrors.
> When a ConflictError (or ReadConflictError) occurs, the transaction should
> fall into an "uncommitable" state.  From this state, you can only abort the
> transaction; any attempts to write an object or commit cause another
> ConflictError.

AFAIK, that is the way the current releases of ZODB work.  I'm not
sure what "write an object" means there, though.  I think I know what
"commit" means.  The logic in Connection.commit() raises
ReadConflictError upon an attempt to commit an object that previously
suffered a conflict during the transaction.  How does that differ from
what you want?

If you know of a case where this doesn't work, please open a Collector
report.  I can *believe* there's a problem -- I just don't know of one
(intended preconditions and postconditions aren't documented, and the
implementation has grown so many clever(?) special cases I'm rarely
100% sure of what it's trying to accomplish).

> Then, only code that can guarantee that the attempted
> transaction is complete should actually abort the transaction, and
> fortunately ZPublisher fits that role.  Today, the abort is implicit, and
> that's the mistake that has caused us to litter the code with knowledge of
> ConflictErrors.  With the "uncommitable" state, it would not matter if the
> application swallows ConflictErrors.
> 
> That said, I was surprised to discover that Python 2.3 implements hasattr this
> way (from bltinmodule.c):
> 
>        v = PyObject_GetAttr(v, name);
>        if (v == NULL) {
>                PyErr_Clear();
>                Py_INCREF(Py_False);
>                return Py_False;
>        }
>        Py_DECREF(v);
>        Py_INCREF(Py_True);
>        return Py_True;

hasattr has always been like that (that exact code, or a workalike
spelling), and that's always how hasattr has been documented to work
(the oldest docs I have handy are for Python 1.4).

> It should not swallow all errors,

Guido doesn't agree; hoping that Python will change here is probably
futile (Guido has said several times that hasattr() should never raise
an exception, and that matches how it's always been documented and
implemented).

> especially now that descriptors make computed attributes quite common.
> getattr() only recently started catching only AttributeErrors, but apparently
> hasattr is lagging behind.

The implementation of 2-argument getattr() didn't match the docs until
Python 2.2, when someone finally noticed that the implementation
didn't match the docs.  Then the getattr() implementation was
repaired, to match the docs.  The docs have never been clear (and
still aren't, IMO) about what 3-argument getattr() does with
exceptions.  I think the current implementation is the most reasonable
one, with 3-argument getattr()  propagating any exceptions the
2-argument form would have propagated, except for AttributeError --
but the docs don't really say that's what 3-arg getattr() does.

> I suggest the consistency between getattr and hasattr should be fixed in Python,
> not Zope.

They weren't intended to treat exceptions the same way, so an argument
based on inconsistency in that area won't make progress.  As I said
recently in zodb-dev,

    Zope and ZODB do incredibly complex stuff as side effects in what Guido
    surely thought of as "tiny hooks".  He had in mind that hasattr()
might look for
    a key in a dict or list, and suppress a harmless KeyError or IndexError,
    not start World War III and then send an endless sequence of Terminators
    back in time to change the outcome <wink>.

BTW, there's a ton of existing code that relies on hasattr()
suppressing "things like" KeyError and IndexError, so I doubt Python
would change here even if Guido agreed hasattr() was wrong to suppress
all exceptions.  But that debate belongs on python-dev.


More information about the Zope-Dev mailing list