Subclassing in C

Iker Arizmendi iker at research.att.com
Wed Sep 15 00:30:08 CEST 2004


After some more poking around I think I've found a solution
to my problem. But although it works, it doesn't look like
the right way to do things.

In short, I updated the tp_getattro method of my subclass to
explicitly check its base class for the presence of any
attributes it doesn't know about. Eg.

    static PyObject*
    PyGrm_GetAttro(PyObject *obj, PyObject* name)
    {
       PyObject* r = Py_FindMethod(PyGrmReplace_methods, obj,
                                   PyString_AsString(name));
        if( PyErr_Occurred() )
        {
            if( PyErr_ExceptionMatches(PyExc_AttributeError) )
            {
                PyErr_Clear();
                PyTypeObject* t = obj->ob_type;
                return t->tp_base->tp_getattro(obj, name);
            }
        }
        return r;
    }

Again, in this case this works just fine as I know I'm only
interested in the one base class. But if I had a deeper
inheritance hierarchy I would have to do the lookup
across each base class myself.


Regards,
Iker


Iker Arizmendi wrote:
> After placing a printf statement in both of these methods
> 
>     fsmpy_getattro
>     PyGrm_GetAttro
> 
> I notice that when calling the "type" method of my
> subclass like so:
> 
>     g = grm.GrmReplace(...)
>     print g.type()
> 
> only the PyGrm_GetAttro method is called. I expected
> that the call to Py_FindMethod would eventually call
> fsmpy_getattro since my subclass doesn't have a method
> called "type" (here the "type" method has nothing to do
> with Python types, it's just what we happen to call
> one of our methods).
> 
> 
> Regards,
> Iker
> 
> 
> Iker Arizmendi wrote:
> 
>> Michael Hudson wrote:
>>
>>> Iker Arizmendi <iker at research.att.com> writes:
>>>
>>>> (*) The class I'm deriving from is defined in another
>>>> extension module and it has its own tp_getattr method.
>>>
>>>
>>>
>>>
>>> Hmm.  I take it *it's* tp_getattr[o] method isn't
>>> PyObject_GenericGetAttr then?
>>>
>>> Then your initial post makes more sense; I'm afraid I don't see
>>> any obvious reason for PyObject_GenericGetAttr to crash.
>>
>>
>>
>> Ah! I missed the trailing 'o' and set tp_getattr, not
>> tp_getattro as you suggested. However, after setting it
>> correctly (I think), things still don't behave as I would
>> expect (eg, the subclass has the union of its methods and
>> the base class methods). The complete code is pretty long
>> so I'm not sure I can post it (most of it deals with non
>> Python stuff) - but here's a more complete piece that
>> relates to setting up the classes.
>>
>> The base class (defined in a separated module) looks
>> like so:
>>
>>     static PyMethodDef fsmpymethods[] =
>>     {
>>        {
>>          "type", (PyCFunction) fsmpytype,
>>           METH_NOARGS,
>>           "type() -> string\n\nReturn FSM type."
>>        },
>>        ...
>>        {
>>            NULL, NULL, 0, NULL
>>        }
>>     };
>>
>>     static PyObject*
>>     fsmpy_getattro(PyObject *obj, PyObject* name)
>>     {
>>         return Py_FindMethod(fsmpymethods, obj,
>>                              PyString_AsString(name));
>>     }
>>
>>     PyTypeObject FSMPyType =
>>     {
>>        PyObject_HEAD_INIT(NULL)
>>        0,
>>        "fsm.Fsm",
>>        sizeof(FSMPyObject),
>>        0,
>>        fsmpy_dealloc,                             /*tp_dealloc*/
>>        0,                                         /*tp_print*/
>>        0,                                         /*tp_getattr*/
>>        0,                                         /*tp_setattr*/
>>        fsmpy_getattro,                            /*tp_getattro*/
>>        ...
>>        0,                                         /*tp_setattro*/
>>        0,                                         /*tp_as_buffer*/
>>        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
>>     ...
>>     };
>>
>> The base class doesn't have a new or init method as the author
>> provides an explicit "create" method.
>>
>> The subclass that I've put together now looks like so (note
>> that I now use the tp_getattro method, and cleared both
>> tp_methods and tp_members):
>>
>>    static PyMethodDef PyGrmReplace_methods[] =
>>    {
>>        {
>>           "mutate", (PyCFunction)PyGrmReplace_mutate,
>>           METH_VARARGS|METH_KEYWORDS,
>>           "mutate(fsm1 [, mode]) -> fsm\n"
>>        },
>>        ...
>>        {NULL}
>>    };
>>
>>    static PyObject*
>>    PyGrm_GetAttro(PyObject *obj, PyObject* name)
>>    {
>>       return Py_FindMethod(PyGrmReplace_methods, obj,
>>                            PyString_AsString(name));
>>    }
>>
>>    static PyTypeObject PyGrmReplaceType =
>>    {
>>       PyObject_HEAD_INIT(NULL)
>>       0,                               /* ob_size */
>>       "grm.GrmReplace",                /* tp_name */
>>        sizeof(PyFsmObject),            /* tp_basicsize */
>>        0,                              /* tp_itemsize */
>>        PyGrmReplace_dealloc,           /* tp_dealloc */
>>        0,                              /* tp_print */
>>        0,                              /* tp_getattr */
>>        0,                              /* tp_setattr */
>>        ...
>>        PyGrm_GetAttro,                 /* tp_getattro */
>>        0,                              /* tp_setattro */
>>        0,                              /* tp_as_buffer */
>>        Py_TPFLAGS_DEFAULT,             /* tp_flags */
>>        "GrmReplace objects",           /* tp_doc */
>>        ...
>>        0,                              /* tp_methods */
>>        0,                              /* tp_members */
>>        ...
>>        PyGrmReplace_init,              /* tp_init */
>>        0,                              /* tp_alloc */
>>        PyGrmReplace_new,               /* tp_new */
>>    };
>>
>>    PyMODINIT_FUNC
>>    initgrm(void)
>>    {
>>       PyGrmReplaceType.tp_new = PyGrmReplace_new;
>>       PyGrmReplaceType.tp_base = pointerToFSMPyType;
>>       if (PyType_Ready(&PyGrmReplaceType) < 0)
>>           return;
>>
>>       PyObject* m = Py_InitModule3("grm", grm_methods,
>>                    "Python binding for the AT&T GRM library");
>>       Py_INCREF(&PyGrmReplaceType);
>>       PyModule_AddObject(m, "GrmReplace", (PyObject*)&PyGrmReplaceType);
>>    }
>>
>> And the result of this is that an instance of grm.GrmReplace
>> makes the mutate method available, but doesn't have the
>> type() method from the fsm.Fsm class. Is there some "canonical"
>> form for implementing this?
>>
>> Thanks again,
>> Iker
>>
>>
>>>
>>>
>>>> Here's the stack trace (via Valgrind), just in case:
>>>
>>>
>>>
>>>
>>> A stack trace from gdb (i.e. with line numbers) would have been more
>>> use.
>>>
>>> I think you have two options here: (a) make friends with gdb or (b)
>>> post [a link to, maybe] complete code.
>>>
>>> Cheers,
>>> mwh
>>>



More information about the Python-list mailing list