[Python-Dev] tp_(get|set)attro? inheritance bug?

Gustavo J. A. M. Carneiro gjc at inescporto.pt
Fri Jun 11 13:58:07 EDT 2004

  Documentation says, regarding tp_getattr:
This field is inherited by subtypes together with tp_getattro: a subtype
inherits both tp_getattr and tp_getattro from its base type when the
subtype's tp_getattr and tp_getattro are both NULL.

  Implementation disagrees, at least in cvs head, but the effect of the
bug (non-inheritance of tp_getattr) happens in 2.3.3.  Follow with me:

In function type_new (typeobject.c) line 1927:
	/* Special case some slots */
	if (type->tp_dictoffset != 0 || nslots > 0) {
		if (base->tp_getattr == NULL && base->tp_getattro == NULL)
			type->tp_getattro = PyObject_GenericGetAttr;
		if (base->tp_setattr == NULL && base->tp_setattro == NULL)
			type->tp_setattro = PyObject_GenericSetAttr;
...later in the same function... line 

	/* Initialize the rest */
	if (PyType_Ready(type) < 0) {
		return NULL;

Inside PyType_Ready(), line 3208:
	for (i = 1; i < n; i++) {
		PyObject *b = PyTuple_GET_ITEM(bases, i);
		if (PyType_Check(b))
			inherit_slots(type, (PyTypeObject *)b);

Inside inherit_slots, line (3056):
	if (type->tp_getattr == NULL && type->tp_getattro == NULL) {
		type->tp_getattr = base->tp_getattr;
		type->tp_getattro = base->tp_getattro;
	if (type->tp_setattr == NULL && type->tp_setattro == NULL) {
		type->tp_setattr = base->tp_setattr;
		type->tp_setattro = base->tp_setattro;

  So, if you have followed through, you'll notice that type_new first
sets tp_getattro = GenericGetAttr, in case 'base' has neither tp_getattr
nor tp_getattro.
  So, you are thinking that there is no problem.  If base has
tp_getattr, that code path won't be execute.  The problem is with
multiple inheritance.  In type_new, 'base' is determined by calling
best_base().  But the selected base may not have tp_getattr, while
another might have.  In this case, setting tp_getattro based on
information from the wrong base precludes the slot from being inherited
from the right base.  This is happening in pygtk, unfortunately.

  One possible solution would be to move the first code block to after
the PyType_Ready() call.

Gustavo João Alves Marques Carneiro
<gjc at inescporto.pt> <gustavo at users.sourceforge.net>
The universe is always one step beyond logic.

More information about the Python-Dev mailing list