python / python/cpython

`get_original_bases` does not return what `cls.__orig_bases__` returns

Open
#122,988 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

docs topic-typing
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

The types documentation states for the types.get_original_bases function:

For classes that have an __orig_bases__ attribute, this function returns the value of cls.__orig_bases__. For classes without the __orig_bases__ attribute, cls.__bases__ is returned.

I need the functionality of cls.__orig_bases__, but need to use types.get_original_bases to make the type checker happy.

A short MRE:

from types import get_original_bases
from typing import Generic, TypeVar

T = TypeVar("T")


class One(Generic[T]):
    pass


class Two(One[int]):
    pass


class Three(Two):
    pass


assert get_original_bases(One) == One.__orig_bases__
assert get_original_bases(Two) == Two.__orig_bases__
assert get_original_bases(Three) == Three.__orig_bases__

# Traceback (most recent call last):
#   File "c:\<cut>\test.py", line 34, in <module>
#     assert get_original_bases(Three) == Three.__orig_bases__
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# AssertionError

This is a easy solve, I would be happy to add a PR. Change here:

    try:
-       return cls.__dict__.get("__orig_bases__", cls.__bases__)
+       return getattr(cls, "__orig_bases__", cls.__bases__)
    except AttributeError:
        raise TypeError(
            f"Expected an instance of type, not {type(cls).__name__!r}"
        ) from None

To show this works:

from typing import Generic, TypeVar

T = TypeVar("T")


class One(Generic[T]):
    pass


class Two(One[int]):
    pass


class Three(Two):
    pass


def better_get_original_bases(cls):
    try:
        return getattr(cls, "__orig_bases__", cls.__bases__)
    except AttributeError:
        raise TypeError(
            f"Expected an instance of type, not {type(cls).__name__!r}"
        ) from None


assert better_get_original_bases(One) == One.__orig_bases__
assert better_get_original_bases(Two) == Two.__orig_bases__
assert better_get_original_bases(Three) == Three.__orig_bases__

# <No output>
CPython versions tested on:

3.12

Operating systems tested on:

Windows

Linked PRs
  • gh-156917

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Lib/types.py at get_original_bases and reproduce the inheritance example from the issue, including the Three class case. Done means the function matches the documented original-bases behavior and regression coverage verifies the inherited attribute case; review linked PR gh-156917 before starting.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.