Subclassing in C
Iker Arizmendi
iker at research.att.com
Tue Sep 14 18:30:08 EDT 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