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

typing/Generic __orig_class__ availability during __init__ in 3.6 vs 3.7 #658

Closed
baltus-atomicrules opened this issue Jul 18, 2019 · 9 comments

Comments

@baltus-atomicrules
Copy link

baltus-atomicrules commented Jul 18, 2019

I 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

(I am comparing 3.6.7 with 3.7.0)

I've 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
@ilevkivskyi
Copy link
Member

Yes, this is unfortunate, but as you know, this is undocumented internal attribute that you are using totally at your own risk. So unless you have a simple idea how to fix this, I don't think there is anything we can do here.

Stewori added a commit to Stewori/pytypes that referenced this issue Oct 23, 2019
… more. Exposed workaround for python/typing#658 as new public API get_orig_class. Added doc to quick manual for this new function.
@Stewori
Copy link

Stewori commented Oct 23, 2019

@baltus-atomicrules
For the record, you can use pytypes.get_orig_class to solve this.
Note: It's only available in master as of this writing. (I hope to make an new release soon.)

@mitar
Copy link
Contributor

mitar commented Sep 30, 2021

this is undocumented internal attribute that you are using totally at your own risk.

Sure, but that makes also typing_inspect fail inside constructor: ilevkivskyi/typing_inspect#35

@JelleZijlstra
Copy link
Member

We can't change what typing looks like in 3.6 or 3.7, so I don't think there's anything actionable in this issue.

@mitar
Copy link
Contributor

mitar commented Oct 18, 2021

I mean, the same still holds true in 3.8, 3.9, 3.10. So the issue in all of those that inside __init__ you cannot access __orig_class__. The issue was only reported when 3.7 was out, but it is still valid.

achimnol added a commit to lablup/backend.ai-manager that referenced this issue Dec 27, 2021
We just fallback to `typing.Any` and make an explicit type-cast, instead of
resolving the generic type argument at runtime to automatically get
the reference to `uuid_subtype_func`.

Here is the story:

Currently Python does not allow introspection of generic type arguments
in runtime.  For some limited cases, we could refer `__orig_bases__`, but
somehow it does not work for us and it is a known limitation that we
cann t resolve it early in the object lifecycle.
(ref: https://stackoverflow.com/questions/57706180, python/typing#658)

Moreover, SQLAlchemy's `TypeDecorator` also involves its own metaclass logic
from `TraversibleType`, which makes `__orig_class__` unavailable.
achimnol added a commit to lablup/backend.ai-manager that referenced this issue Dec 27, 2021
* setup: Set the minimum mypy version to 0.930
* fix: New typecheck errors due to stricter enum validation
* fix: Workaround limitation of type variable usage in ClassVar

  We just fallback to `typing.Any` and make an explicit type-cast, instead of
  resolving the generic type argument at runtime to automatically get
  the reference to `uuid_subtype_func`.

  Here is the story:

  Currently Python does not allow introspection of generic type arguments
  in runtime.  For some limited cases, we could refer `__orig_bases__`, but
  somehow it does not work for us and it is a known limitation that we
  cann t resolve it early in the object lifecycle.
  (ref: https://stackoverflow.com/questions/57706180, python/typing#658)

  Moreover, SQLAlchemy's `TypeDecorator` also involves its own metaclass logic
  from `TraversibleType`, which makes `__orig_class__` unavailable.
achimnol added a commit to lablup/backend.ai-manager that referenced this issue Dec 27, 2021
* setup: Set the minimum mypy version to 0.930
* fix: New typecheck errors due to stricter enum validation
* fix: Workaround limitation of type variable usage in ClassVar

  We just fallback to `typing.Any` and make an explicit type-cast, instead of
  resolving the generic type argument at runtime to automatically get
  the reference to `uuid_subtype_func`.

  Here is the story:

  Currently Python does not allow introspection of generic type arguments
  in runtime.  For some limited cases, we could refer `__orig_bases__`, but
  somehow it does not work for us and it is a known limitation that we
  cann t resolve it early in the object lifecycle.
  (ref: https://stackoverflow.com/questions/57706180, python/typing#658)

  Moreover, SQLAlchemy's `TypeDecorator` also involves its own metaclass logic
  from `TraversibleType`, which makes `__orig_class__` unavailable.

Backported-From: main (22.03)
Backported-To: 21.09
achimnol added a commit to lablup/backend.ai-manager that referenced this issue Dec 27, 2021
* setup: Set the minimum mypy version to 0.930
* fix: New typecheck errors due to stricter enum validation
* fix: Workaround limitation of type variable usage in ClassVar

  We just fallback to `typing.Any` and make an explicit type-cast, instead of
  resolving the generic type argument at runtime to automatically get
  the reference to `uuid_subtype_func`.

  Here is the story:

  Currently Python does not allow introspection of generic type arguments
  in runtime.  For some limited cases, we could refer `__orig_bases__`, but
  somehow it does not work for us and it is a known limitation that we
  cann t resolve it early in the object lifecycle.
  (ref: https://stackoverflow.com/questions/57706180, python/typing#658)

  Moreover, SQLAlchemy's `TypeDecorator` also involves its own metaclass logic
  from `TraversibleType`, which makes `__orig_class__` unavailable.

Backported-From: main (22.03)
Backported-To: 21.03
@Gobot1234
Copy link
Contributor

This has been annoying me as of late, couldn't we just have it not be available in __new__ but available in __init__?

@fzyzcjy
Copy link

fzyzcjy commented Dec 31, 2022

Hi, is there any updates? Thanks

@LostInDarkMath
Copy link

I still face this issue with Python 3.11. Are there any valid workarounds for this?

@srittau
Copy link
Collaborator

srittau commented Oct 19, 2023

Please report any issues related to issues with the typing module to CPython's issue tracker: https://github.com/python/cpython/issues, where the module is maintained nowadays.

@python python locked as off-topic and limited conversation to collaborators Oct 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants