Mailing List Archive

Odd types.get_original_bases() behavior for classes with generic bases but no type arguments
(Apologies if this has already been discussed. I tried to search and didn't find
anything relevant.)

I was playing around with 3.12.0b4 this evening and noticed an odd (to me, at
least) behavior with types.get_original_bases(). I hesitate to call it a bug
because I think I understand *why* it's behaving this way—I'm just not sure it's
desirable.


>>> T = typing.TypeVar("T")
>>> class FirstBase(typing.Generic[T]):
... pass
...
>>> class SecondBase(typing.Generic[T]):
... pass
...
>>> class First(FirstBase[int]):
... pass
...
>>> class Second(SecondBase[int]):
... pass
...
>>> class Example(First, Second):
... pass
...
>>> types.get_original_bases(Example)
(FirstBase[int],)
>>> Example.__bases__
(First, Second)
>>> types.get_original_bases(First)
(FirstBase[int],)

In other words, types.get_original_bases(Example) is returning the original base
types for First, rather than its own.

I believe this happens because __orig_bases__ is only set when one or more bases
are not types. In this case both bases are types, so Example doesn't get its own
__orig_bases__. Then when types.get_original_bases() tries to get __orig_bases__
with getattr(cls, "__orig_bases__") or something morally equivalent, it searches
the MRO and finds __orig_bases__ on First.

The same thing also happens if all the bases are "bare" generic types.

>>> class First(typing.Generic[T]):
... pass
...
>>> class Second(typing.Generic[T]):
... pass
...
>>> class Example(First, Second):
... pass
...
>>> types.get_original_bases(Example)
(typing.Generic[~T],)

As I said, I'm not clear if this is a bug, or just an unfortunate but intended
behavior. I would personally expect types.get_original_bases() to check if the
type has its *own* __orig_bases__ attribute, and to fall back to __bases__
otherwise. The way it works today makes it unnecessarily difficult to write a
function that, for example, recurses down a type's inheritance tree inspecting
the original bases—I currently have to work around this behavior via hacks like
checking "__orig_bases__" in cls.__dict__ or any(types.get_original_bases(cls)
== types.get_original_bases(base) for base in cls.__bases__). (Unless I'm
missing some simpler solution.)

Is this something that could (should?) be addressed before 3.12 lands?

Thanks,
Chris Bouchard
--
https://mail.python.org/mailman/listinfo/python-list
Re: Odd types.get_original_bases() behavior for classes with generic bases but no type arguments [ In reply to ]
> >>> Example.__bases__
> (First, Second)

Sorry, that should have been

>>> Example.__bases__
(<class '__main__.First'>, <class '__main__.Second'>)

That's what I get for writing an example from memory rather than copying it from
a REPL session. It doesn't change the point I wanted to make, though.

Thanks,
Chris Bouchard
--
https://mail.python.org/mailman/listinfo/python-list