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
Assignment to __bases__ of direct object subclasses #37817
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2003-01-21.22:45:40.000>labels= ['interpreter-core', 'type-feature', '3.11']
title='Assignment to __bases__ of direct object subclasses'updated_at=<Date2022-03-23.00:08:37.846>user='https://bugs.python.org/glchapman'
I'm not entirely sure this is a bug, but I think it is
surprising:
Python 2.3a1 (#38, Dec 31 2002, 17:53:59) [MSC
v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more
information.
>>> classA(object):
... pass
...
>>> classB(object):
... pass
...
>>> B.__bases__= (A,)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: __bases__ assignment: 'A' deallocator differs
from 'object'
It seems like you should be able to change the
__bases__ of a new-style class (implemented in
Python) which inherits directly from object to another
new-style class. (Will the deallocator issue ever come
into play if the only types involved are HEAPTYPES and
object as the ultimate base?)
I agree this is a bit surprising. When I was writing this
code I went for the conservative-as-possible approach as I
didn't want to introduce instabilities to Python.
It certainly may be that I've overdone it. In this case I
probably have; if the tp_dealloc of the class being adjusted
is subtype_dealloc and the tp_dealloc that ultimately gets
invoked is the same we're probably OK. But I'm not sure and
it's been a while since I thought about this.
It also happens that my motivating use-case for this isn't
troubled by this restriction.
I don't understand your last, parenthetical, comment.
HEAPTYPES as such doesn't come into it, does it?
You might be right that we don't need to worry about
tp_dealloc if the ultimate solid_base doesn't change and all
the tp_deallocs on the way there are subtype_dealloc...
glchapman commented on Jan 23, 2003
glchapmanmannequin
MannequinAuthor
More actions
Logged In: YES
user_id=86307
Sorry about the parenthetical comment; I think what I was trying
to say is basically what you have in your last paragraph.
As for use cases, I don't have any myself (I ran into this with
some test code for a metaclass which "overrides" __bases__).
However, grepping through the standard library, I note that one
place where assignment to __bases__ is used is in
xmlrpclib.SlowParser. It appears to me that if SlowParser and
xmllib.XMLParser (neither of which has a base class) were
converted to new-style classes, the assignment to __bases__
would generate this exception. Of course, that shouldn't be too
hard to work around if that turns out to be necessary.
Are you interested in working up a patch for this? Hacking
this kind of stuff requires motivation I'm not sure I can
drum up in time for 2.3...
glchapman commented on Feb 5, 2003
glchapmanmannequin
MannequinAuthor
More actions
Logged In: YES
user_id=86307
Well, I wrote a small patch which I'm attaching. However, I
can't say that I'm partcularly confident in it. It works for the
simple cases I've been able to think of (and for
test_descr.py), but looking at typeobject.c, I get the nagging
feeling that a lot more tests are required to be sure it's OK.
The problem is, I'm just not sure what they (the tests) are.
An issue that hadn't occurred to me before is that in the first example both
A and B have a __dict__ (and __weakref__) descriptor, and in the second
B doesn't. Should B's __dict__ descriptor be removed on the
__bases__ assignment?
glchapman commented on Mar 28, 2005
glchapmanmannequin
MannequinAuthor
More actions
Logged In: YES
user_id=86307
Still here -- sorry not to reply sooner. I couldn't
actually remember what my patch was supposed to do, or more
specifically I couldn't remember what it did to check that
this sort of change in __bases__ was safe. So, anyway, I
finally got around to looking at the patch again, and at
typeobject.c, and I can say that I'm less sure of the
subtleties involved now than I was then. Anyway, with that
caveat, what you suggest sounds reasonable enough, though I
suppose you'd have to reinsert a dict descriptor if
__bases__ was later changed back to (object,). (It looks
like the patch would have supported changing __bases__ back
to (object,), though perhaps it shouldn't.)
It seems to me nobody is particularly troubled by this
limitation on assignment to __bases__ (perhaps you know
differently?). Maybe it's best just to close this as "not a
bug."
Well, my motivation is to better understand what's going on
in typeobject.c. There are a number of unclear sections (to
put it mildly) and undocumented assumptions and this makes
it hard to work out whether things (like this) are bugs or not.
I'm now fairly confident that the line
"compatible_for_assigment(new_base, self->tp_base)" is
asking the right question (and, thus, your patch is not
really correct, though I doubt it's actually unsafe). The
spelling is a bit funny though.
This issue in and of itself isn't that high a priority, but
understanding (and documenting that understanding) of
typeobject.c does seem worth working on...
lorph commented on Jul 22, 2008
lorphmannequin
Mannequin
More actions
Is anyone still working on this? It seems like an oddity of python that
has been a stumbling block for me to create a super reload.
I've found that i am able to bypass this problem by creating the
following definition:
Another 3 and a bit years on <wink> I still think my comment http://bugs.python.org/msg14169 is the crux of the issue. It's even
relevant to your class object(object): pass hack!
I'm not at all likely to work on this any time soon myself.
There were several causes that prevented assignment.
tp_free is different -- PyObject_Free and PyObject_GC_Del. But there is no need to check this for __bases__ assignment, because only tp_free of the original class is used, and it is not changed after assignment.
The Py_TPFLAGS_HAVE_GC flag is different in old and new bases. For __bases__ assignment, this was wrong check. The only forbidden case is if the flag is set in a new base but not set in the original (derived) class. The same is for other flags.
This fixes the case when class A has empty __slots__.
This is not enough to make the original example working, because A has __dict__ and __weakref__, and therefore its base size differs from the size of object. But B has __dict__ and __weakref__ with the same offset and don't add new fields, so it may be possible make the assignment even more lenient.
Activity
glchapman commentedon Jan 21, 2003
I'm not entirely sure this is a bug, but I think it is
surprising:
It seems like you should be able to change the
__bases__ of a new-style class (implemented in
Python) which inherits directly from object to another
new-style class. (Will the deallocator issue ever come
into play if the only types involved are HEAPTYPES and
object as the ultimate base?)
mwhudson commentedon Jan 22, 2003
Logged In: YES
user_id=6656
I agree this is a bit surprising. When I was writing this
code I went for the conservative-as-possible approach as I
didn't want to introduce instabilities to Python.
It certainly may be that I've overdone it. In this case I
probably have; if the tp_dealloc of the class being adjusted
is subtype_dealloc and the tp_dealloc that ultimately gets
invoked is the same we're probably OK. But I'm not sure and
it's been a while since I thought about this.
It also happens that my motivating use-case for this isn't
troubled by this restriction.
I don't understand your last, parenthetical, comment.
HEAPTYPES as such doesn't come into it, does it?
You might be right that we don't need to worry about
tp_dealloc if the ultimate solid_base doesn't change and all
the tp_deallocs on the way there are subtype_dealloc...
glchapman commentedon Jan 23, 2003
Logged In: YES
user_id=86307
Sorry about the parenthetical comment; I think what I was trying
to say is basically what you have in your last paragraph.
As for use cases, I don't have any myself (I ran into this with
some test code for a metaclass which "overrides" __bases__).
However, grepping through the standard library, I note that one
place where assignment to __bases__ is used is in
xmlrpclib.SlowParser. It appears to me that if SlowParser and
xmllib.XMLParser (neither of which has a base class) were
converted to new-style classes, the assignment to __bases__
would generate this exception. Of course, that shouldn't be too
hard to work around if that turns out to be necessary.
mwhudson commentedon Jan 31, 2003
Logged In: YES
user_id=6656
Are you interested in working up a patch for this? Hacking
this kind of stuff requires motivation I'm not sure I can
drum up in time for 2.3...
glchapman commentedon Feb 5, 2003
Logged In: YES
user_id=86307
Well, I wrote a small patch which I'm attaching. However, I
can't say that I'm partcularly confident in it. It works for the
simple cases I've been able to think of (and for
test_descr.py), but looking at typeobject.c, I get the nagging
feeling that a lot more tests are required to be sure it's OK.
The problem is, I'm just not sure what they (the tests) are.
mwhudson commentedon Feb 5, 2003
Logged In: YES
user_id=6656
Mhmm. That looks OK to me (after realizing that solid_base
worries about __slots__).
But I don't know how one can be sure :-(
mwhudson commentedon Mar 16, 2005
Logged In: YES
user_id=6656
Two years on, I think about this again. Still here? :)
The motivating thought is that:
and
should be equivalent.
An issue that hadn't occurred to me before is that in the first example both
A and B have a __dict__ (and __weakref__) descriptor, and in the second
B doesn't. Should B's __dict__ descriptor be removed on the
__bases__ assignment?
glchapman commentedon Mar 28, 2005
Logged In: YES
user_id=86307
Still here -- sorry not to reply sooner. I couldn't
actually remember what my patch was supposed to do, or more
specifically I couldn't remember what it did to check that
this sort of change in __bases__ was safe. So, anyway, I
finally got around to looking at the patch again, and at
typeobject.c, and I can say that I'm less sure of the
subtleties involved now than I was then. Anyway, with that
caveat, what you suggest sounds reasonable enough, though I
suppose you'd have to reinsert a dict descriptor if
__bases__ was later changed back to (object,). (It looks
like the patch would have supported changing __bases__ back
to (object,), though perhaps it shouldn't.)
It seems to me nobody is particularly troubled by this
limitation on assignment to __bases__ (perhaps you know
differently?). Maybe it's best just to close this as "not a
bug."
mwhudson commentedon Mar 29, 2005
Logged In: YES
user_id=6656
Well, my motivation is to better understand what's going on
in typeobject.c. There are a number of unclear sections (to
put it mildly) and undocumented assumptions and this makes
it hard to work out whether things (like this) are bugs or not.
I'm now fairly confident that the line
"compatible_for_assigment(new_base, self->tp_base)" is
asking the right question (and, thus, your patch is not
really correct, though I doubt it's actually unsafe). The
spelling is a bit funny though.
This issue in and of itself isn't that high a priority, but
understanding (and documenting that understanding) of
typeobject.c does seem worth working on...
lorph commentedon Jul 22, 2008
Is anyone still working on this? It seems like an oddity of python that
has been a stumbling block for me to create a super reload.
I've found that i am able to bypass this problem by creating the
following definition:
However, this feels like an ugly hack.
mwhudson commentedon Jul 22, 2008
Another 3 and a bit years on <wink> I still think my comment
http://bugs.python.org/msg14169 is the crux of the issue. It's even
relevant to your class object(object): pass hack!
I'm not at all likely to work on this any time soon myself.
16 remaining items
erlend-aasland commentedon Jan 15, 2024
erlend-aasland commentedon Jan 16, 2024
Opening as per request of @serhiy-storchaka
Keniis0712 commentedon Jul 29, 2025
I have the same problem when I do some meta-programming. Is there any plans to fix/change it?
pythongh-37817: Allow assignment to __bases__ of direct subclasses of…
pythongh-37817: Allow assignment to __bases__ of direct subclasses of…
serhiy-storchaka commentedon Aug 9, 2025
There were several causes that prevented assignment.
tp_freeis different --PyObject_FreeandPyObject_GC_Del. But there is no need to check this for__bases__assignment, because onlytp_freeof the original class is used, and it is not changed after assignment.Py_TPFLAGS_HAVE_GCflag is different in old and new bases. For__bases__assignment, this was wrong check. The only forbidden case is if the flag is set in a new base but not set in the original (derived) class. The same is for other flags.This fixes the case when class
Ahas empty__slots__.This is not enough to make the original example working, because
Ahas__dict__and__weakref__, and therefore its base size differs from the size ofobject. ButBhas__dict__and__weakref__with the same offset and don't add new fields, so it may be possible make the assignment even more lenient.serhiy-storchaka commentedon Aug 9, 2025
Fixed also the original case.
gh-37817: Allow assignment to __bases__ of direct subclasses of built…