You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Py_TYPE(lhs)->tp_as_mapping->mp_subscript is not the same as Dict_Type.tp_as_mapping->mp_subscript when it should be #132284
A dict subclass which does not override __getitem__ (like collections.Counter) should have Dict_Type.tp_as_mapping->mp_subscript as its Py_TYPE(lhs)->tp_as_mapping->mp_subscript, but they are not the same.
Apparently, update_one_slot() in typeobject.c always sets the slot, whether or not it is already set to a perfectly acceptable value, like in the case of dict/Counter. That seems like something worth improving.
FWIW, this happened for Counter but not for dict because the former comes from a class definition and the latter is a static type. update_one_slot() is called by fixup_slot_dispatchers(), which is only called by type_new_impl(), which is only called by type_new() (the value of PyType_Type.tp_new). That is called by type() when a class definition is turned into a class, like in the case of Counter. It is not called for static types. Thus mp_subscript is updated by update_one_slot() for Counter but dict keeps its original mp_subscript.
Activity
ericsnowcurrently commentedon Apr 8, 2025
Apparently,
update_one_slot()in typeobject.c always sets the slot, whether or not it is already set to a perfectly acceptable value, like in the case ofdict/Counter. That seems like something worth improving.FWIW, this happened for
Counterbut not fordictbecause the former comes from a class definition and the latter is a static type.update_one_slot()is called byfixup_slot_dispatchers(), which is only called bytype_new_impl(), which is only called bytype_new()(the value ofPyType_Type.tp_new). That is called bytype()when a class definition is turned into a class, like in the case ofCounter. It is not called for static types. Thusmp_subscriptis updated byupdate_one_slot()forCounterbutdictkeeps its originalmp_subscript.tom-pytel commentedon Apr 9, 2025
The following small change leaves previous C method slots unchanged in subclasses if not overridden (including
Counter.__getitem__):Subclass
__getitem__()performance even increases a bit from:to:
Rationale for the checks is as follows:
generic == NULLbecause not sure of behavior with slots that loop several times liketp_getattr,tp_setattrandtp_richcompare.Py_IS_TYPE(descr, &PyMethodDescr_Type)because only if setting pre-existing builtin C method.*ptr == ((PyMethodDescrObject *)descr)->d_method->ml_methchecks both*ptr != NULLand stuff like:Passes all the tests but I don't have too much experience in this part of the code so there may be some other check or two still needed?
Do you want this as a PR?
ericsnowcurrently commentedon Apr 9, 2025
That makes sense. Go ahead and make a PR.
gh-132284: Don't wrap base PyCFunction slots on class creation if not…
picnixz commentedon Jul 12, 2025
I marked it as a bug but I don't know if we want to backport this as it could be an important behavior change for some users.
tom-pytel commentedon Jul 12, 2025
Would backporting give any benefit? And I don't know if I would classify this as a bug as it doesn't really break anything.
picnixz commentedon Jul 12, 2025
I may have misunderstood the issue then. Let's consider it as a feature.