classification
Title: typing.Generic breaks __init_subclass__
Type: behavior Stage: resolved
Components: Versions: Python 3.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Ilya.Kulakov, levkivskyi, wrmsr
Priority: normal Keywords:

Created on 2017-11-28 22:27 by Ilya.Kulakov, last changed 2018-04-02 22:03 by wrmsr. This issue is now closed.

Messages (9)
msg307182 - (view) Author: Ilya Kulakov (Ilya.Kulakov) * Date: 2017-11-28 22:27
When superclass inherits from Generic, attributes set for a subclass are incorrectly propagated to its superclass.

Without Generic attribute access raises an exception:

    class X:
        def __init_subclass__(cls, **kwargs):
            super().__init_subclass__(**kwargs)
            cls.attr = 42

    class Y(X):
        pass

    Y.attr  # 42
    X.attr  # AttributeError

With Generic it does not:

    import typing

    T = typing.TypeVar('T')

    class X(typing.Generic[T]):
        def __init_subclass__(cls, **kwargs):
            super().__init_subclass__(**kwargs)
            cls.attr = 42

    class Y(X):
        pass

    Y.attr  # 42
    X.attr  # 42
msg307185 - (view) Author: Ilya Kulakov (Ilya.Kulakov) * Date: 2017-11-28 22:48
This issue is more server that I expected: it doesn't just propagate value to superclasses, but overrides them. The last subclass created by Python runtime will overwrite value for the whole chain.
msg307187 - (view) Author: Ilya Kulakov (Ilya.Kulakov) * Date: 2017-11-28 22:52
Current workaround is

    class X(typing.Generic[T] if typing.TYPE_CHECKING else object):
msg307189 - (view) Author: Ilya Kulakov (Ilya.Kulakov) * Date: 2017-11-28 23:08
Nah, that's a bad one: you cannot use Generic classes as intended by specifying types.

It looks like it happens because cls._grog is not yet set properly by the time __init_subclass__ is called.
msg307192 - (view) Author: Ilya Kulakov (Ilya.Kulakov) * Date: 2017-11-28 23:54
That's a better workaround:

    class X(typing.Generic[T]):
        def __init_subclass__(cls, **kwargs):
            super(typing.GenericMeta, cls).__setattr__('_gorg', cls)
            super().__init_subclass__(**kwargs)
msg312309 - (view) Author: Ivan Levkivskyi (levkivskyi) * (Python committer) Date: 2018-02-18 13:07
FWIW, this is fixed in 3.7 by PEP 560, providing a separate fix for 3.6 is not easy, and you have a good workaround, so I propose to close this issue.
msg314824 - (view) Author: Will T (wrmsr) Date: 2018-04-02 20:08
I believe I'm experiencing a related bug in the new (3.7) version unfortunately. The current version of typing.Generic.__init_subclass__ isn't chaining to super(Generic, cls).__init_subclass__, meaning Generic's position in class bases can prevent subsequent bases from working properly. I can currently work around it with this unsightly hack but it's obviously suboptimal:

    _old_generic_init_subclass = object.__getattribute__(ta.Generic, '__init_subclass__').__func__
    @classmethod  # noqa
    def _new_generic_init_subclass(cls, *args, **kwargs):  # noqa
        _old_generic_init_subclass(cls, *args, **kwargs)
        super(ta.Generic, cls).__init_subclass__(*args, **kwargs)
    ta.Generic.__init_subclass__ = _new_generic_init_subclass  # noqa
msg314825 - (view) Author: Ivan Levkivskyi (levkivskyi) * (Python committer) Date: 2018-04-02 20:18
Thanks Will!

I think this is actually a different (although very similar issue). It looks like it is easy to fix. Could you please open a separate issue (and mention it here)? Also could you please provide a typical example when this breaks so that I can add an appropriate test with the fix?
msg314829 - (view) Author: Will T (wrmsr) Date: 2018-04-02 22:03
Done: https://bugs.python.org/issue33207 - thanks for the quick response!
History
Date User Action Args
2018-04-02 22:03:02wrmsrsetmessages: + msg314829
2018-04-02 20:18:40levkivskyisetmessages: + msg314825
2018-04-02 20:08:48wrmsrsetnosy: + wrmsr
messages: + msg314824
2018-02-18 13:07:43levkivskyisetstatus: open -> closed
resolution: fixed
messages: + msg312309

stage: resolved
2017-12-01 22:23:06levkivskyisetnosy: + levkivskyi
2017-11-28 23:54:31Ilya.Kulakovsetmessages: + msg307192
2017-11-28 23:08:36Ilya.Kulakovsetmessages: + msg307189
2017-11-28 22:52:50Ilya.Kulakovsetmessages: + msg307187
2017-11-28 22:48:14Ilya.Kulakovsetmessages: + msg307185
2017-11-28 22:27:43Ilya.Kulakovcreate