classification
Title: PyType_Ready doesn't ensure that all bases are ready
Type: enhancement Stage: needs patch
Components: Interpreter Core Versions: Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: abusalimov, amaury.forgeotdarc, belopolsky, benjamin.peterson, ronaldoussoren, rupole
Priority: normal Keywords:

Created on 2008-07-27 15:14 by rupole, last changed 2014-10-29 07:58 by pitrou.

Messages (9)
msg70322 - (view) Author: Roger Upole (rupole) Date: 2008-07-27 15:14
If a type's tp_base has not been initialized yet, PyType_Ready calls 
itself for tp_base.  However, it doesn't do the same for members of 
tp_bases.  The inheritance determinations assume that all bases are 
ready, in particular that tp_mro is not null.
msg70324 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2008-07-27 15:24
I believe that's because the bases are supposed to be ready at the time
a subclass is created.
msg70376 - (view) Author: Roger Upole (rupole) Date: 2008-07-29 02:55
If that were the case, it wouldn't need to call PyType_Ready for 
tp_base either.  From stepping thru the code, there are several places 
in the interpreter core that PyType_Ready is called for types whose 
tp_base has not been initialized yet.
msg70378 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-07-29 07:19
PyType_Ready is called for each class in tp_bases.
This is done in typeobject.c::best_base()
Isn't it the case in your program?
msg70380 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-07-29 08:40
Forget my last remark: it applies to heap types (created with
type_new()) and not to static types.

However, it seems that a non-ready class in tp_bases can happen only
when an extension type inherits from another extension type.
It is good practice to call PyType_Ready() on every type you define
(otherwise tp_methods doesn't work); doesn't this answer the initial
problem?
msg71482 - (view) Author: Roger Upole (rupole) Date: 2008-08-19 20:58
This doesn't address the discrepancy between tp_base and tp_bases.
If multiple bases are used, it's no longer 'good practice', it's an
absolute requirement.  IMO, it should call PyType_Ready for all bases, 
or none of them.

Since the code assumes that all bases have been
initialized, it could at least ASSERT so, rather than crashing deep
within the mro calculations.
msg107502 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2010-06-10 23:38
My knowledge may be out of date, but I thought multiple inheritance was only supported at the python level.  If this is still the case, then no initialization check is needed. (You cannot get an uninitialized type at python level.)  An extra defensive assert is usually not a bad thing in the code, but in this particular case one would need a loop with checks and it does not seem justified.

-1
msg193636 - (view) Author: Ronald Oussoren (ronaldoussoren) * (Python committer) Date: 2013-07-24 11:08
I don't know if multiple inheritance is explicitly supported at the C level, but it is possible to create an extension type with multiple base classes in tp_bases.
msg230189 - (view) Author: Eldar Abusalimov (abusalimov) * Date: 2014-10-28 23:59
It is possible to get a partially initialized class from inside a custom mro(). And extending such type results in passing NULL to PySequence_List inside mro_implementation, which in turn leads to PyErr_BadInternalCall. #22735 has a test reproducing it (http://bugs.python.org/file37036) and a corresponding fix of mro_implementation (http://bugs.python.org/file37038).

However, I'm not sure that the proper way is to call PyType_Ready on each uninitialized class from tp_bases. In particular, the test case from the link above would end up with infinite recursion (PyType_Ready(cls) -> mro(cls) -> class X(cls): ... -> PyType_Ready(X) -> PyType_Ready(cls) -> ...). Moreover, whether a type is initialized or not is determized by checking its tp_dict, which is initialized before filling in tp_mro, so that may be the test above is not the case of Roger.
History
Date User Action Args
2014-10-29 07:58:15pitrousetversions: + Python 3.5, - Python 3.2
2014-10-28 23:59:51abusalimovsetnosy: + abusalimov
messages: + msg230189
2013-07-24 11:08:19ronaldoussorensetnosy: + ronaldoussoren
messages: + msg193636
2010-06-10 23:38:45belopolskysetversions: + Python 3.2, - Python 2.6, Python 2.5, Python 2.4
nosy: + belopolsky

messages: + msg107502

type: enhancement
stage: needs patch
2008-08-19 20:58:04rupolesetmessages: + msg71482
2008-07-29 08:40:37amaury.forgeotdarcsetmessages: + msg70380
2008-07-29 07:20:00amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg70378
2008-07-29 02:55:54rupolesetmessages: + msg70376
2008-07-27 15:24:49benjamin.petersonsetnosy: + benjamin.peterson
messages: + msg70324
2008-07-27 15:14:04rupolecreate