Skip to content

(Re-)Make typing.Generic.__orig_class__ accessible in __init__ #123777

@XieJiSS

Description

@XieJiSS

Bug report

Bug description:

baltus-atomicrules observe that when executing __init__ in a Generic class, the value of __orig_class__ is available in version 3.6 but not in 3.7. And others found that this is still the case from 3.8 to 3.12. To be specific, mitar reproduced on 3.8 to 3.10, LostInDarkMath reproduced on 3.11, and I reproduced on 3.12.

baltus-atomicrules attached a simple example that defines a Generic class and then accesses __orig_class__ in the associated __init__ method.

Simple.py.txt

N = TypeVar('N')
class ZZ(Generic[N]):
    def __init__(self):
        print("__orig_class__ is: ", self.__orig_class__)

In 3.6, this code runs fine:

In [42]: import Simple
In [51]: a = Simple.ZZ[int]
In [56]: b = a()
__orig_class__ is:  Simple.ZZ[int]

In 3.7+ however, the value of __orig_class__ is not available:

In [4]: import Simple
In [5]: a = Simple.ZZ[int]
In [6]: b = a()
AttributeError                            Traceback (most recent call last)
<ipython-input-6-52a6c422c17e> in <module>
----> 1 b = a()
/tool/pandora64/.package/python-3.7.0/lib/python3.7/typing.py in __call__(self, *args, **kwargs)
    668             raise TypeError(f"Type {self._name} cannot be instantiated; "
    669                             f"use {self._name.lower()}() instead")
--> 670         result = self.__origin__(*args, **kwargs)
    671         try:
    672             result.__orig_class__ = self
/proj/emu_scratch4/users/dbaltus/python/experiment/Simple.py in __init__(self)
      4 class ZZ(Generic[N]):
      5     def __init__(self):
----> 6         print("__orig_class__ is: ", self.__orig_class__)
AttributeError: 'ZZ' object has no attribute '__orig_class__'

> /proj/emu_scratch4/users/dbaltus/python/experiment/Simple.py(6)__init__()
      2
      3 N = TypeVar('N')
      4 class ZZ(Generic[N]):
      5     def __init__(self):
----> 6         print("__orig_class__ is: ", self.__orig_class__)
ipdb> q

This occurs because in 3.7, the __call__ method of _GenericAlias does not set the value of __orig_class__ until after __init__ is called. (It is done differently in 3.6).

    def __call__(self, *args, **kwargs):
        if not self._inst:
            raise TypeError(f"Type {self._name} cannot be instantiated; "
                            f"use {self._name.lower()}() instead")
        result = self.__origin__(*args, **kwargs)
        try:
            result.__orig_class__ = self
        except AttributeError:
            pass
        return result

srittau told us to open a new issue here, and locked the old conversation. I think it would be helpful to open a issue here for tracking and back-ref that old issue: python/typing#658

Workaround

Future people encountering this can use pytypes.get_orig_class as a workaround. Source code: https://github.com/Stewori/pytypes/blob/3846f1be3e44892c8e60f750f7581cbddd8be383/pytypes/type_util.py#L182

CPython versions tested on:

3.12

Operating systems tested on:

Linux, macOS

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions