@@ -130,6 +130,12 @@ static int add_subclass(PyTypeObject*, PyTypeObject*);
130130static void remove_subclass (PyTypeObject * , PyTypeObject * );
131131static void update_all_slots (PyTypeObject * );
132132
133+ typedef int (* update_callback )(PyTypeObject * , void * );
134+ static int update_subclasses (PyTypeObject * type , PyObject * name ,
135+ update_callback callback , void * data );
136+ static int recurse_down_subclasses (PyTypeObject * type , PyObject * name ,
137+ update_callback callback , void * data );
138+
133139static int
134140mro_subclasses (PyTypeObject * type , PyObject * temp )
135141{
@@ -2031,6 +2037,10 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
20312037 type -> tp_name );
20322038 return -1 ;
20332039 }
2040+ /* XXX Example of how I expect this to be used...
2041+ if (update_subclasses(type, name, invalidate_cache, NULL) < 0)
2042+ return -1;
2043+ */
20342044 if (PyObject_GenericSetAttr ((PyObject * )type , name , value ) < 0 )
20352045 return -1 ;
20362046 return update_slot (type , name );
@@ -4993,7 +5003,7 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
49935003 return res ;
49945004}
49955005
4996- /* Common code for update_these_slots () and fixup_slot_dispatchers(). This
5006+ /* Common code for update_slots_callback () and fixup_slot_dispatchers(). This
49975007 does some incredibly complex thinking and then sticks something into the
49985008 slot. (It sees if the adjacent slotdefs for the same slot have conflicting
49995009 interests, and then stores a generic wrapper or a specific function into
@@ -5068,51 +5078,15 @@ update_one_slot(PyTypeObject *type, slotdef *p)
50685078 return p ;
50695079}
50705080
5071- static int recurse_down_subclasses (PyTypeObject * type , slotdef * * pp ,
5072- PyObject * name );
5073-
5074- /* In the type, update the slots whose slotdefs are gathered in the pp0 array,
5075- and then do the same for all this type's subtypes. */
5081+ /* In the type, update the slots whose slotdefs are gathered in the pp array.
5082+ This is a callback for update_subclasses(). */
50765083static int
5077- update_these_slots (PyTypeObject * type , slotdef * * pp0 , PyObject * name )
5084+ update_slots_callback (PyTypeObject * type , void * data )
50785085{
5079- slotdef * * pp ;
5086+ slotdef * * pp = ( slotdef * * ) data ;
50805087
5081- for (pp = pp0 ; * pp ; pp ++ )
5088+ for (; * pp ; pp ++ )
50825089 update_one_slot (type , * pp );
5083- return recurse_down_subclasses (type , pp0 , name );
5084- }
5085-
5086- /* Update the slots whose slotdefs are gathered in the pp array in all (direct
5087- or indirect) subclasses of type. */
5088- static int
5089- recurse_down_subclasses (PyTypeObject * type , slotdef * * pp , PyObject * name )
5090- {
5091- PyTypeObject * subclass ;
5092- PyObject * ref , * subclasses , * dict ;
5093- int i , n ;
5094-
5095- subclasses = type -> tp_subclasses ;
5096- if (subclasses == NULL )
5097- return 0 ;
5098- assert (PyList_Check (subclasses ));
5099- n = PyList_GET_SIZE (subclasses );
5100- for (i = 0 ; i < n ; i ++ ) {
5101- ref = PyList_GET_ITEM (subclasses , i );
5102- assert (PyWeakref_CheckRef (ref ));
5103- subclass = (PyTypeObject * )PyWeakref_GET_OBJECT (ref );
5104- assert (subclass != NULL );
5105- if ((PyObject * )subclass == Py_None )
5106- continue ;
5107- assert (PyType_Check (subclass ));
5108- /* Avoid recursing down into unaffected classes */
5109- dict = subclass -> tp_dict ;
5110- if (dict != NULL && PyDict_Check (dict ) &&
5111- PyDict_GetItem (dict , name ) != NULL )
5112- continue ;
5113- if (update_these_slots (subclass , pp , name ) < 0 )
5114- return -1 ;
5115- }
51165090 return 0 ;
51175091}
51185092
@@ -5175,7 +5149,8 @@ update_slot(PyTypeObject *type, PyObject *name)
51755149 }
51765150 if (ptrs [0 ] == NULL )
51775151 return 0 ; /* Not an attribute that affects any slots */
5178- return update_these_slots (type , ptrs , name );
5152+ return update_subclasses (type , name ,
5153+ update_slots_callback , (void * )ptrs );
51795154}
51805155
51815156/* Store the proper functions in the slot dispatches at class (type)
@@ -5203,6 +5178,51 @@ update_all_slots(PyTypeObject* type)
52035178 }
52045179}
52055180
5181+ /* recurse_down_subclasses() and update_subclasses() are mutually
5182+ recursive functions to call a callback for all subclasses,
5183+ but refraining from recursing into subclasses that define 'name'. */
5184+
5185+ static int
5186+ update_subclasses (PyTypeObject * type , PyObject * name ,
5187+ update_callback callback , void * data )
5188+ {
5189+ if (callback (type , data ) < 0 )
5190+ return -1 ;
5191+ return recurse_down_subclasses (type , name , callback , data );
5192+ }
5193+
5194+ static int
5195+ recurse_down_subclasses (PyTypeObject * type , PyObject * name ,
5196+ update_callback callback , void * data )
5197+ {
5198+ PyTypeObject * subclass ;
5199+ PyObject * ref , * subclasses , * dict ;
5200+ int i , n ;
5201+
5202+ subclasses = type -> tp_subclasses ;
5203+ if (subclasses == NULL )
5204+ return 0 ;
5205+ assert (PyList_Check (subclasses ));
5206+ n = PyList_GET_SIZE (subclasses );
5207+ for (i = 0 ; i < n ; i ++ ) {
5208+ ref = PyList_GET_ITEM (subclasses , i );
5209+ assert (PyWeakref_CheckRef (ref ));
5210+ subclass = (PyTypeObject * )PyWeakref_GET_OBJECT (ref );
5211+ assert (subclass != NULL );
5212+ if ((PyObject * )subclass == Py_None )
5213+ continue ;
5214+ assert (PyType_Check (subclass ));
5215+ /* Avoid recursing down into unaffected classes */
5216+ dict = subclass -> tp_dict ;
5217+ if (dict != NULL && PyDict_Check (dict ) &&
5218+ PyDict_GetItem (dict , name ) != NULL )
5219+ continue ;
5220+ if (update_subclasses (subclass , name , callback , data ) < 0 )
5221+ return -1 ;
5222+ }
5223+ return 0 ;
5224+ }
5225+
52065226/* This function is called by PyType_Ready() to populate the type's
52075227 dictionary with method descriptors for function slots. For each
52085228 function slot (like tp_repr) that's defined in the type, one or more
@@ -5216,10 +5236,11 @@ update_all_slots(PyTypeObject* type)
52165236 In the latter case, the first slotdef entry encoutered wins. Since
52175237 slotdef entries are sorted by the offset of the slot in the
52185238 PyHeapTypeObject, this gives us some control over disambiguating
5219- between competing slots: the members of PyHeapTypeObject are listed from most
5220- general to least general, so the most general slot is preferred. In
5221- particular, because as_mapping comes before as_sequence, for a type
5222- that defines both mp_subscript and sq_item, mp_subscript wins.
5239+ between competing slots: the members of PyHeapTypeObject are listed
5240+ from most general to least general, so the most general slot is
5241+ preferred. In particular, because as_mapping comes before as_sequence,
5242+ for a type that defines both mp_subscript and sq_item, mp_subscript
5243+ wins.
52235244
52245245 This only adds new descriptors and doesn't overwrite entries in
52255246 tp_dict that were previously defined. The descriptors contain a
0 commit comments