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) *  |
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) *  |
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! |
|
|