Skip to content

Commit

Permalink
Be stricter about access to generic vars from class
Browse files Browse the repository at this point in the history
This behaviour was intentionally chosen in python#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
  • Loading branch information
hauntsaninja committed Nov 3, 2024
1 parent 1f200dd commit 280f565
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 15 deletions.
12 changes: 5 additions & 7 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,13 +1087,11 @@ def analyze_class_attribute_access(
def_vars.add(node.node.info.self_type)
typ_vars = set(get_type_vars(t))
if def_vars & typ_vars:
# Exception: access on Type[...], including first argument of class methods is OK.
if not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit:
if node.node.is_classvar:
message = message_registry.GENERIC_CLASS_VAR_ACCESS
else:
message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
mx.msg.fail(message, mx.context)
if node.node.is_classvar:
message = message_registry.GENERIC_CLASS_VAR_ACCESS
else:
message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
mx.msg.fail(message, mx.context)
t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True)
# Erase non-mapped variables, but keep mapped ones, even if there is an error.
# In the above example this means that we infer following types:
Expand Down
22 changes: 16 additions & 6 deletions test-data/unit/check-classvar.test
Original file line number Diff line number Diff line change
Expand Up @@ -282,17 +282,27 @@ main:2: note: Revealed type is "builtins.int"
main:3: error: Cannot assign to class variable "x" via instance

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

A.x # E: Access to generic class variables is ambiguous
A.x = 1 # E: Access to generic class variables is ambiguous
A[int].x # E: Access to generic class variables is ambiguous
def main(A_T: Type[A]):
A.x # E: Access to generic class variables is ambiguous
A.x = 1 # E: Access to generic class variables is ambiguous
A[int].x # E: Access to generic class variables is ambiguous

A_T.x # E: Access to generic class variables is ambiguous
A_T.x = 1 # E: Access to generic class variables is ambiguous
A_T[int].x # E: Value of type "Type[A[Any]]" is not indexable

a = A
a.x # E: Access to generic class variables is ambiguous
a.x = 1 # E: Access to generic class variables is ambiguous
a[int].x # E: The type "Type[A[Any]]" is not generic and not indexable

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

A.x # E: Access to generic class variables is ambiguous
A.x = 1 # E: Access to generic class variables is ambiguous
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -2225,7 +2225,7 @@ class C(Generic[T]):
x: T
@classmethod
def get(cls) -> T:
return cls.x # OK
return cls.x # E: Access to generic instance variables via class is ambiguous

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

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

0 comments on commit 280f565

Please sign in to comment.