Skip to content

Commit 62d43b8

Browse files
committed
Be stricter about access to generic vars from class
This behaviour was intentionally chosen in #6418 I agree with Ivan's comment there that it's similar to how mypy allows instantiation of `type[Abstract]` -- but that's a thing that I want to explore disallowing. Let's see primer
1 parent 1f200dd commit 62d43b8

File tree

3 files changed

+23
-15
lines changed

3 files changed

+23
-15
lines changed

mypy/checkmember.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,13 +1087,11 @@ def analyze_class_attribute_access(
10871087
def_vars.add(node.node.info.self_type)
10881088
typ_vars = set(get_type_vars(t))
10891089
if def_vars & typ_vars:
1090-
# Exception: access on Type[...], including first argument of class methods is OK.
1091-
if not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit:
1092-
if node.node.is_classvar:
1093-
message = message_registry.GENERIC_CLASS_VAR_ACCESS
1094-
else:
1095-
message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
1096-
mx.msg.fail(message, mx.context)
1090+
if node.node.is_classvar:
1091+
message = message_registry.GENERIC_CLASS_VAR_ACCESS
1092+
else:
1093+
message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
1094+
mx.msg.fail(message, mx.context)
10971095
t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True)
10981096
# Erase non-mapped variables, but keep mapped ones, even if there is an error.
10991097
# In the above example this means that we infer following types:

test-data/unit/check-classvar.test

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,17 +282,27 @@ main:2: note: Revealed type is "builtins.int"
282282
main:3: error: Cannot assign to class variable "x" via instance
283283

284284
[case testClassVarWithGeneric]
285-
from typing import ClassVar, Generic, TypeVar
285+
from typing import ClassVar, Generic, TypeVar, Type
286286
T = TypeVar('T')
287287
class A(Generic[T]):
288288
x: ClassVar[T] # E: ClassVar cannot contain type variables
289289
@classmethod
290290
def foo(cls) -> T:
291-
return cls.x # OK
291+
return cls.x # E: Access to generic class variables is ambiguous
292292

293-
A.x # E: Access to generic class variables is ambiguous
294-
A.x = 1 # E: Access to generic class variables is ambiguous
295-
A[int].x # E: Access to generic class variables is ambiguous
293+
def main(A_T: Type[A]):
294+
A.x # E: Access to generic class variables is ambiguous
295+
A.x = 1 # E: Access to generic class variables is ambiguous
296+
A[int].x # E: Access to generic class variables is ambiguous
297+
298+
A_T.x # E: Access to generic class variables is ambiguous
299+
A_T.x = 1 # E: Access to generic class variables is ambiguous
300+
A_T[int].x # E: Value of type "Type[A[Any]]" is not indexable
301+
302+
a = A
303+
a.x # E: Access to generic class variables is ambiguous
304+
a.x = 1 # E: Access to generic class variables is ambiguous
305+
a[int].x # E: The type "Type[A[Any]]" is not generic and not indexable
296306

297307
class Bad(A[int]):
298308
pass
@@ -311,7 +321,7 @@ class A(Generic[T, U]):
311321
x: ClassVar[Union[T, Tuple[U, Type[U]]]] # E: ClassVar cannot contain type variables
312322
@classmethod
313323
def foo(cls) -> Union[T, Tuple[U, Type[U]]]:
314-
return cls.x # OK
324+
return cls.x # E: Access to generic class variables is ambiguous
315325

316326
A.x # E: Access to generic class variables is ambiguous
317327
A.x = 1 # E: Access to generic class variables is ambiguous

test-data/unit/check-generics.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,7 +2225,7 @@ class C(Generic[T]):
22252225
x: T
22262226
@classmethod
22272227
def get(cls) -> T:
2228-
return cls.x # OK
2228+
return cls.x # E: Access to generic instance variables via class is ambiguous
22292229

22302230
x = C.x # E: Access to generic instance variables via class is ambiguous
22312231
reveal_type(x) # N: Revealed type is "Any"
@@ -2288,7 +2288,7 @@ reveal_type(b) # N: Revealed type is "__main__.B"
22882288

22892289
def g(t: Type[Maker[T]]) -> T:
22902290
if bool():
2291-
return t.x
2291+
return t.x # E: Access to generic instance variables via class is ambiguous
22922292
return t.get()
22932293
bb = g(B)
22942294
reveal_type(bb) # N: Revealed type is "__main__.B"

0 commit comments

Comments
 (0)