Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assignment to __bases__ of direct object subclasses #37817

Open
glchapman mannequin opened this issue Jan 21, 2003 · 18 comments
Open

Assignment to __bases__ of direct object subclasses #37817

glchapman mannequin opened this issue Jan 21, 2003 · 18 comments
Labels
3.11 interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@glchapman
Copy link
Mannequin

glchapman mannequin commented Jan 21, 2003

BPO 672115
Nosy @mwhudson, @amauryfa, @benjaminp, @florentx, @ambv, @kynan, @maggyero
Files
  • typeobject.c.diff
  • 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 = None
    closed_at = None
    created_at = <Date 2003-01-21.22:45:40.000>
    labels = ['interpreter-core', 'type-feature', '3.11']
    title = 'Assignment to __bases__ of direct object subclasses'
    updated_at = <Date 2022-03-23.00:08:37.846>
    user = 'https://bugs.python.org/glchapman'

    bugs.python.org fields:

    activity = <Date 2022-03-23.00:08:37.846>
    actor = 'maggyero'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Interpreter Core']
    creation = <Date 2003-01-21.22:45:40.000>
    creator = 'glchapman'
    dependencies = []
    files = ['751']
    hgrepos = []
    issue_num = 672115
    keywords = []
    message_count = 18.0
    messages = ['14163', '14164', '14165', '14166', '14167', '14168', '14169', '14170', '14171', '70132', '70133', '86225', '88508', '88569', '106633', '154116', '189987', '270220']
    nosy_count = 16.0
    nosy_names = ['mwh', 'glchapman', 'amaury.forgeotdarc', 'lorph', 'benjamin.peterson', 'memeplex', 'farcat', 'flox', 'Hari.Krishna.Dara', 'lukasz.langa', 'kynan', 'eli.collins', 'aventurella', 'krzysiekj', 'borisompol', 'maggyero']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'needs patch'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue672115'
    versions = ['Python 3.11']

    @glchapman
    Copy link
    Mannequin Author

    glchapman mannequin commented Jan 21, 2003

    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.
    >>> class A(object):
    ...     pass
    ...
    >>> class B(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?)

    @glchapman glchapman mannequin assigned mwhudson Jan 21, 2003
    @glchapman glchapman mannequin added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Jan 21, 2003
    @glchapman glchapman mannequin assigned mwhudson Jan 21, 2003
    @glchapman glchapman mannequin added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Jan 21, 2003
    @mwhudson
    Copy link

    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
    Copy link
    Mannequin Author

    glchapman mannequin commented 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
    Copy link

    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
    Copy link
    Mannequin Author

    glchapman mannequin commented 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
    Copy link

    mwhudson commented 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
    Copy link

    Logged In: YES
    user_id=6656

    Two years on, I think about this again. Still here? :)

    The motivating thought is that:

    class A(object): pass
    class B(object): pass
    B.__bases__ = (A,)

    and

    class A(object): pass
    class B(A): pass

    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
    Copy link
    Mannequin Author

    glchapman mannequin commented 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
    Copy link

    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
    Copy link
    Mannequin

    lorph mannequin commented 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:

    class object(object):pass

    However, this feels like an ugly hack.

    @mwhudson
    Copy link

    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.

    @farcat
    Copy link
    Mannequin

    farcat mannequin commented Apr 21, 2009

    in my project i need to be able to let the user dynamically make and
    remove inheritance relationships between classes and in my testing i
    think i have run into this issue assigning to __bases__. the

    class object(object):
        pass

    trick seems to work, but i can't really oversee the consequenses. I also
    saw another variation which might be the same issue:

        A= type("A", (object,), {'one': 1})
        B= type("B", (object,), {'two': 2})
        C= type("C", (object,), {'three': 3})
    
        A = type("A",(A,B),{})
        
        print dir(A)
        print A.__bases__
        print '-----------------------------------'
        A.__bases__ = (B,C)
        print dir(A)
        print A.__bases__
        print '-----------------------------------'

    no exceptions, but the second dir(A) shows that A has lost its attribute
    'one'

    if the class object(object) trick is not safe, is there a way to get the
    dynamic inheritance behaviour in another way, e.g. through metaclasses?

    @amauryfa
    Copy link
    Member

    Lars, I think that your question has a simple explanation, and is not
    related to the original issue:

    • On 4th line, the variable named "A" is bound to a new type object.
      This type has no attribute by itself.
    • The first dir(A) displays the attributes of the bases class, which are
      the previous A and B: you see ['one', 'two']
    • The second dir(A) displays the attributes of the bases class, which
      are B and C: you see ['two', 'three']

    The instruction "A = type(...)" causes A to refer to another object; it
    does not change the object previously contained in A.

    @farcat
    Copy link
    Mannequin

    farcat mannequin commented May 30, 2009

    ok, i see what you mean, for me at this time the most important question
    is whta does

    class object(object)
        pass

    do, why can i change baseclasses after i redeclare object this way, and
    will it get me into trouble when i use this to let users dynamically
    define classes and inheritence relationships?

    cheers

    @HariKrishnaDara
    Copy link
    Mannequin

    HariKrishnaDara mannequin commented May 28, 2010

    I just hit up on this same bug and the "class object(object): pass" workaround worked fine. I too would like to know how safe this workaround is, could someone more insightful please comment on this?

    @BreamoreBoy BreamoreBoy mannequin added type-bug An unexpected behavior, bug, or error labels Aug 17, 2010
    @aventurella
    Copy link
    Mannequin

    aventurella mannequin commented Feb 24, 2012

    I too just found the same issue, dunno if it was settled on as being a bug per se.

    "class object(object): pass"

    Also worked for me. I don't feel great about doing that though. So I opted for more verbose name as not to collide with "object"

    I have to wonder, since it's been 9 years, if all of us finding this issue are guilty of doing it wrong =)

    @BreamoreBoy
    Copy link
    Mannequin

    BreamoreBoy mannequin commented May 25, 2013

    Has any work been done on typeobject.c to render this obsolete? Failing that are there any souls brave enough to take on typeobject.c?

    @pitrou pitrou added type-feature A feature request or enhancement and removed type-bug An unexpected behavior, bug, or error labels Jun 11, 2015
    @memeplex
    Copy link
    Mannequin

    memeplex mannequin commented Jul 12, 2016

    I understand Michael's comment http://bugs.python.org/issue672115#msg14169 but I'm failing to see how it's directly related to this issue: as Michael himself said it's even relevant for the object-subclass-object hack or for any hierarchy whatsoever. Is the comment warning about the entire writable-bases idea? It feels to me like the behavior should be consistently extended or consistently banned, but currently some cases work, another cases don't, and there seems to be no clear rationale supporting this state of affairs. Am I wrong?

    @maggyero maggyero mannequin added 3.11 labels Mar 23, 2022
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 9, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    3 participants