Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Allow generic class variables #7906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,11 @@ def analyze_class_attribute_access(itype: Instance,
if isinstance(t, TypeVarType) or get_type_vars(t):
# Exception: access on Type[...], including first argument of class methods is OK.
if not isinstance(get_proper_type(mx.original_type), TypeType):
mx.msg.fail(message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS, 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)

# 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
2 changes: 2 additions & 0 deletions mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
# Generic
GENERIC_INSTANCE_VAR_CLASS_ACCESS = \
'Access to generic instance variables via class is ambiguous' # type: Final
GENERIC_CLASS_VAR_ACCESS = \
'Access to generic class variables is ambiguous' # type: Final
BARE_GENERIC = 'Missing type parameters for generic type {}' # type: Final
IMPLICIT_GENERIC_ANY_BUILTIN = \
'Implicit generic "Any". Use "{}" and specify generic parameters' # type: Final
Expand Down
7 changes: 2 additions & 5 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
Type, UnboundType, TypeVarType, TupleType, TypedDictType, UnionType, Instance, AnyType,
CallableType, NoneType, ErasedType, DeletedType, TypeList, TypeVarDef, SyntheticTypeVisitor,
StarType, PartialType, EllipsisType, UninhabitedType, TypeType, replace_alias_tvars,
CallableArgument, get_type_vars, TypeQuery, union_items, TypeOfAny,
LiteralType, RawExpressionType, PlaceholderType, Overloaded, get_proper_type, ProperType
CallableArgument, TypeQuery, union_items, TypeOfAny, LiteralType, RawExpressionType,
PlaceholderType, Overloaded, get_proper_type, ProperType
)

from mypy.nodes import (
Expand Down Expand Up @@ -311,9 +311,6 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt
self.fail('ClassVar[...] must have at most one type argument', t)
return AnyType(TypeOfAny.from_error)
item = self.anal_type(t.args[0])
if isinstance(item, TypeVarType) or get_type_vars(item):
self.fail('Invalid type: ClassVar cannot be generic', t)
return AnyType(TypeOfAny.from_error)
return item
elif fullname in ('mypy_extensions.NoReturn', 'typing.NoReturn'):
return UninhabitedType(is_noreturn=True)
Expand Down
45 changes: 45 additions & 0 deletions test-data/unit/check-classvar.test
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,48 @@ class A:
[out]
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
T = TypeVar('T')
class A(Generic[T]):
x: ClassVar[T]
@classmethod
def foo(cls) -> T:
return cls.x # OK

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

class Bad(A[int]):
pass
Bad.x # E: Access to generic class variables is ambiguous

class Good(A[int]):
x = 42
reveal_type(Good.x) # N: Revealed type is 'builtins.int'
[builtins fixtures/classmethod.pyi]

[case testClassVarWithNestedGeneric]
from typing import ClassVar, Generic, Tuple, TypeVar, Union, Type
T = TypeVar('T')
U = TypeVar('U')
class A(Generic[T, U]):
x: ClassVar[Union[T, Tuple[U, Type[U]]]]
@classmethod
def foo(cls) -> Union[T, Tuple[U, Type[U]]]:
return cls.x # OK

A.x # E: Access to generic class variables is ambiguous
A.x = 1 # E: Access to generic class variables is ambiguous
A[int, str].x # E: Access to generic class variables is ambiguous

class Bad(A[int, str]):
pass
Bad.x # E: Access to generic class variables is ambiguous

class Good(A[int, str]):
x = 42
reveal_type(Good.x) # N: Revealed type is 'builtins.int'
[builtins fixtures/classmethod.pyi]
18 changes: 0 additions & 18 deletions test-data/unit/semanal-classvar.test
Original file line number Diff line number Diff line change
Expand Up @@ -206,21 +206,3 @@ class B:
pass
[out]
main:4: error: ClassVar can only be used for assignments in class body

[case testClassVarWithGeneric]
from typing import ClassVar, Generic, TypeVar
T = TypeVar('T')
class A(Generic[T]):
x = None # type: ClassVar[T]
[out]
main:4: error: Invalid type: ClassVar cannot be generic

[case testClassVarWithNestedGeneric]
from typing import ClassVar, Generic, List, TypeVar, Union
T = TypeVar('T')
U = TypeVar('U')
class A(Generic[T, U]):
x = None # type: ClassVar[Union[T, List[U]]]
[builtins fixtures/list.pyi]
[out]
main:5: error: Invalid type: ClassVar cannot be generic