bpo-44975: [typing] Support issubclass for ClassVar data members #27883
Conversation
CC @uriyyo please take a look. Thank you. |
LGTM
I have few minor questions
@@ -1396,8 +1396,15 @@ def _get_protocol_attrs(cls): | |||
|
|||
|
|||
def _is_callable_members_only(cls): | |||
attr_names = _get_protocol_attrs(cls) | |||
annotations = getattr(cls, '__annotations__', {}) |
What about using typing.get_type_hints
to resolve annotations for a class?
It will allow having ClassVar
annotated as str:
@runtime_checkable
class P(Protocol):
x: 'ClassVar[int]' = 1
annotations = getattr(cls, '__annotations__', {}) | |
annotations = get_type_hints(cls) |
Good question! I'd considered that too but decided against it for two main reasons:
get_type_hints
is slowget_type_hints
won't work with forward references/ PEP 563 ifClassVar
is not defined in the caller's namespace
Consider the following:
# foo.py
from __future__ import annotations
from typing import *
@runtime_checkable
class X(Protocol):
x: ClassVar[int] = 1
y: SomeUndeclaredType = None
# bar.py
from .foo import X
class Y: ...
# Error! get_type_hints cannot resolve 'ClassVar' and 'SomeUndeclaredType'
issubclass(X, Y)
Nonetheless, your suggestion has reminded me of a basic workaround for string annotations. Thanks.
LGTM
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
|
||
class C: pass | ||
|
||
class D: |
Let's also add a case with the same class prop, but different value. Example:
class E:
x = 2
Because right now all names / values always match. It is not clear whether value is a part of the protocol or not.
class D: | ||
x = 1 | ||
self.assertNotIsSubclass(C, P) | ||
self.assertIsSubclass(D, P) |
One more case that is not covered: same name, different type.
class F:
x = 'a'
It should not pass, but maybe ClassVar
wrapper might break / change that.
x = 1 | ||
self.assertNotIsSubclass(C, P) | ||
self.assertIsSubclass(D, P) | ||
|
Let's also test these classes:
class G:
x: ClassVar[int] = 1
class H:
x: 'ClassVar[int]' = 1
@@ -1394,10 +1394,24 @@ def _get_protocol_attrs(cls): | |||
attrs.add(attr) | |||
return attrs | |||
|
|||
_classvar_prefixes = ("typing.ClassVar[", "t.ClassVar[", "ClassVar[") |
What about raw ClassVar
annotation with generic argument?
https://bugs.python.org/issue44975
The text was updated successfully, but these errors were encountered: