Hi Marc,

On Wed, Dec 28, 2005 at 09:56:43PM +0100, M.-A. Lemburg wrote:
> >>>>> d += 1.2
> >>>>> d
> >> NotImplemented
> 
> The PEP documenting the coercion logic has complete tables
> for what should happen:

Well, '+=' does not invoke coercion at all, with new-style classes like
Decimal.

> Looking at the code in abstract.c the above problem appears
> to be related to the special cases applied to += and *=
> in case both operands cannot deal with the type combination.
>
> In such a case, a check is done whether the operation could
> be interpreted as sequence operation (concat or repeat) and
> then delegated to the appropriate handlers.

Indeed.  The bug was caused by this delegation, which (prior to my
patch) would also return a Py_NotImplemented that would leak through
abstract.c.  My patch is to remove this unnecessary delegation by not
defining sq_concat/sq_repeat for user-defined classes, and restoring the
original expectation that the sq_concat/sq_repeat slots should not
return Py_NotImplemented.  How does this relate to coercion?

> But then again, looking in typeobject.c, the following code
> could be the cause for leaking a NotImplemented singleton
> reference:
> 
> #define SLOT1BINFULL(FUNCNAME, TESTFUNC, SLOTNAME, OPSTR, ROPSTR) \
> static PyObject * \
> FUNCNAME(PyObject *self, PyObject *other) \
> { \
>       static PyObject *cache_str, *rcache_str; \
>       int do_other = self->ob_type != other->ob_type && \
>           other->ob_type->tp_as_number != NULL && \
>           other->ob_type->tp_as_number->SLOTNAME == TESTFUNC; \
>       if (self->ob_type->tp_as_number != NULL && \
>           self->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \
>               PyObject *r; \
>               if (do_other && \
>                   PyType_IsSubtype(other->ob_type, self->ob_type) && \
>                   method_is_overloaded(self, other, ROPSTR)) { \
>                       r = call_maybe( \
>                               other, ROPSTR, &rcache_str, "(O)", self); \
>                       if (r != Py_NotImplemented) \
>                               return r; \
>                       Py_DECREF(r); \
>                       do_other = 0; \
>               } \
>               r = call_maybe( \
>                       self, OPSTR, &cache_str, "(O)", other); \
>               if (r != Py_NotImplemented || \
>                   other->ob_type == self->ob_type) \
>                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> If both types are of the same type, then a NotImplemented returng
> value would be returned.

Indeed, however:

> 
>                       return r; \
>               Py_DECREF(r); \
>       } \
>       if (do_other) { \
>               return call_maybe( \
>                       other, ROPSTR, &rcache_str, "(O)", self); \
>       } \
>       Py_INCREF(Py_NotImplemented); \
>       return Py_NotImplemented; \
> }

This last statement also returns Py_NotImplemented.  So it's expected of
this function to be able to return Py_NotImplemented, isn't it?  The
type slots like nb_add can return Py_NotImplemented; the code that
converts it to a TypeError is in the caller, which is abstract.c.


A bientot,

Armin
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to