This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: tp_(get|set)attro? inheritance bug
Type: behavior Stage:
Components: Interpreter Core Versions: Python 2.6
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: ajaksu2, gustavo, gvanrossum, loewis, mwh
Priority: normal Keywords:

Created on 2004-06-18 22:04 by gustavo, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (7)
msg21219 - (view) Author: Gustavo J. A. M. Carneiro (gustavo) * Date: 2004-06-18 22:04
 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...

        /* Initialize the rest */
        if (PyType_Ready(type) < 0) {
                Py_DECREF(type);
                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.
msg21220 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2004-06-21 05:05
Logged In: YES 
user_id=21627

Guido, is this a bug?
msg21221 - (view) Author: Michael Hudson (mwh) (Python committer) Date: 2004-06-22 09:17
Logged In: YES 
user_id=6656

Without wanting to think about this terribly hard, wouldn't a 
workaround be for pygtk to implement tp_getattro and not 
tp_getattr?  IMO, this is a good idea anyway...
msg21222 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2004-06-23 04:38
Logged In: YES 
user_id=6380

No time to think about this more, but yes, a type should
only implement *one* of tp_getattr and tp_getattro, and only
*one* (best the matching one!) of tp_setattr and
tp_setattro. If you are trying to get different semantics
for getattro and getattr, you're crazy (that's not what the
two are for -- tp_getattr is only there for b/w compat).
msg21223 - (view) Author: Gustavo J. A. M. Carneiro (gustavo) * Date: 2004-06-23 12:30
Logged In: YES 
user_id=908

PyGTK  implements only tp_getattr, none of tp_getattro,
tp_setattr, or tp_setattro.

We already have a workaround in pygtk, but it was my civic
duty to report this bug O:-)
msg82059 - (view) Author: Daniel Diniz (ajaksu2) * (Python triager) Date: 2009-02-14 13:58
Not a bug, then? Will close unless someone argues against it.
msg82061 - (view) Author: Gustavo J. A. M. Carneiro (gustavo) * Date: 2009-02-14 14:07
If this problem does not apply to Python 3.x, by all means go ahead and
close it.  It probably is a bug, but probably not worth fixing;
workaround is simple enough, and affected extensions have already
adapted to it.
History
Date User Action Args
2022-04-11 14:56:04adminsetgithub: 40418
2009-02-14 16:19:08benjamin.petersonsetstatus: open -> closed
resolution: wont fix
2009-02-14 14:07:51gustavosetmessages: + msg82061
2009-02-14 13:58:58ajaksu2setnosy: + ajaksu2
type: behavior
messages: + msg82059
versions: + Python 2.6, - Python 2.3
2004-06-18 22:04:18gustavocreate