[PyKDE] Bug in code for wrapped attributes

Denis S. Otkidach ods at strana.ru
Sat Jul 2 09:19:46 BST 2005


Below is a code generated for wrapped attribute (PyObject* in original
class, SIP_PYOBJECT in SIP spec):

-->8--
static PyObject *var_MyClass_myAttr(PyObject *sipSelf,PyObject *sipPy)
{
        PyObject * sipVal;
        MyClass *sipCpp = reinterpret_cast<MyClass *>(sipGetCppPtr((sipWrapper *)sipSelf,sipClass_MyClass));

        if (!sipCpp)
                return NULL;

        if (sipPy == NULL)
        {
                sipVal = sipCpp -> myAttr;

                Py_XINCREF(sipVal);

                return sipVal;
        }       
        
        sipVal = sipPy;

        if (PyErr_Occurred() != NULL)
        {
                sipBadSetType(sipNm_MyModule_MyClass,sipNm_MyModule_myAttr);
                return NULL;
        }

        Py_INCREF(sipVal); // <-- missed increment
        sipCpp -> myAttr = sipVal;

        Py_INCREF(Py_None);
        return Py_None;
}
-->8--

There is no line marked with "missed increment" in generated code, so
attribute value has incorrect reference count causing strange behavior
and/or segmentation fault.

The story about how I discovered this bug is quite interesting.  I've
written test script looking like the following:

foo = MyModule.Foo()
foo.myAttr = lambda *args: args
bar = foo.createBar()  # bar constructor assignes foo.myAttr to bar.myAttr
def newMyAttr(*args):
    print 'newMyAttr called with', args
bar.doSmth() # this method calls bar.myAttr

I defined newMyAttr as replacement for earlier assigned function. Then I
run the script and so expected output "newMyAttr called with (2, 1, 2)".
 Just a minute later I discovered that I'd actually forgotten to assign
newMyAttr to bar.myAttr!  Then why it's called?!  Did Python conjectured
that I wanted to assign newMyAttr itself?  Looked like a mistery.  I
added "print bar.myAttr" before and after newMyAttr definition and saw:

converter: <function <lambda> at 0x30c994>
converter: <function newMyAttr at 0x30c994>

Hey, these objects have the same address!  Farther experiments proved my
guess that lambda object was destroyed.  In fact, after changing

foo.myAttr = lambda *args: args

into

f = lambda *args: args
foo.myAttr = f

the problem disappeared.  Putting "print sys.getrefcount(f)" showed 2
when 3 expected (f itself, foo.myAttr, and argument to getrefcount).

-- 
Denis S. Otkidach
http://www.python.ru/      [ru]




More information about the PyQt mailing list