diff --git a/mypy/checker.py b/mypy/checker.py index 7b3f6b19498c..6d95a78f974e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -892,8 +892,6 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) self.fail(msg, defn) if note: self.note(note, defn) - if defn.is_class and isinstance(arg_type, CallableType): - arg_type.is_classmethod_class = True elif isinstance(arg_type, TypeVarType): # Refuse covariant parameter type variables # TODO: check recursively for inner type variables diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e1f101fd86f3..c7cb08ffd6eb 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -602,16 +602,16 @@ def check_call(self, callee: Type, args: List[Expression], return callee.ret_type, callee if (callee.is_type_obj() and callee.type_object().is_abstract - # Exceptions for Type[...] and classmethod first argument - and not callee.from_type_type and not callee.is_classmethod_class + # Exception for Type[...] + and not callee.from_type_type and not callee.type_object().fallback_to_any): type = callee.type_object() self.msg.cannot_instantiate_abstract_class( callee.type_object().name(), type.abstract_attributes, context) elif (callee.is_type_obj() and callee.type_object().is_protocol - # Exceptions for Type[...] and classmethod first argument - and not callee.from_type_type and not callee.is_classmethod_class): + # Exception for Type[...] + and not callee.from_type_type): self.chk.fail('Cannot instantiate protocol class "{}"' .format(callee.type_object().name()), context) @@ -737,6 +737,9 @@ def analyze_type_type_callee(self, item: Type, context: Context) -> Type: for c in callee.items()]) if callee: return callee + # We support Type of namedtuples but not of tuples in general + if isinstance(item, TupleType) and item.fallback.type.fullname() != 'builtins.tuple': + return self.analyze_type_type_callee(item.fallback, context) self.msg.unsupported_type_type(item, context) return AnyType(TypeOfAny.from_error) @@ -1133,9 +1136,7 @@ def check_arg(self, caller_type: Type, original_caller_type: Type, caller_type.is_type_obj() and (caller_type.type_object().is_abstract or caller_type.type_object().is_protocol) and isinstance(callee_type.item, Instance) and - (callee_type.item.type.is_abstract or callee_type.item.type.is_protocol) and - # ...except for classmethod first argument - not caller_type.is_classmethod_class): + (callee_type.item.type.is_abstract or callee_type.item.type.is_protocol)): self.msg.concrete_only_call(callee_type, context) elif not is_subtype(caller_type, callee_type): if self.chk.should_suppress_optional_error([caller_type, callee_type]): diff --git a/mypy/plugin.py b/mypy/plugin.py index 7928f91b3678..ae910e766023 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -14,7 +14,7 @@ from mypy.tvar_scope import TypeVarScope from mypy.types import ( Type, Instance, CallableType, TypedDictType, UnionType, NoneTyp, TypeVarType, - AnyType, TypeList, UnboundType, TypeOfAny + AnyType, TypeList, UnboundType, TypeOfAny, TypeType, ) from mypy.messages import MessageBuilder from mypy.options import Options @@ -93,7 +93,7 @@ def anal_type(self, t: Type, *, raise NotImplementedError @abstractmethod - def class_type(self, info: TypeInfo) -> Type: + def class_type(self, self_type: Type) -> Type: raise NotImplementedError @abstractmethod diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 2aa25b59da0e..8329a87facb2 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -495,23 +495,6 @@ def _add_init(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute], [attribute.argument(ctx) for attribute in attributes if attribute.init], NoneTyp() ) - for stmt in ctx.cls.defs.body: - # The type of classmethods will be wrong because it's based on the parent's __init__. - # Set it correctly. - if isinstance(stmt, Decorator) and stmt.func.is_class: - func_type = stmt.func.type - if isinstance(func_type, CallableType): - func_type.arg_types[0] = ctx.api.class_type(ctx.cls.info) - if isinstance(stmt, OverloadedFuncDef) and stmt.is_class: - func_type = stmt.type - if isinstance(func_type, Overloaded): - class_type = ctx.api.class_type(ctx.cls.info) - for item in func_type.items(): - item.arg_types[0] = class_type - if stmt.impl is not None: - assert isinstance(stmt.impl, Decorator) - if isinstance(stmt.impl.func.type, CallableType): - stmt.impl.func.type.arg_types[0] = class_type class MethodAdder: diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 998f82387b75..621614d4d66f 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -92,23 +92,6 @@ def transform(self) -> None: args=[attr.to_argument(info) for attr in attributes if attr.is_in_init], return_type=NoneTyp(), ) - for stmt in self._ctx.cls.defs.body: - # Fix up the types of classmethods since, by default, - # they will be based on the parent class' init. - if isinstance(stmt, Decorator) and stmt.func.is_class: - func_type = stmt.func.type - if isinstance(func_type, CallableType): - func_type.arg_types[0] = self._ctx.api.class_type(self._ctx.cls.info) - if isinstance(stmt, OverloadedFuncDef) and stmt.is_class: - func_type = stmt.type - if isinstance(func_type, Overloaded): - class_type = ctx.api.class_type(ctx.cls.info) - for item in func_type.items(): - item.arg_types[0] = class_type - if stmt.impl is not None: - assert isinstance(stmt.impl, Decorator) - if isinstance(stmt.impl.func.type, CallableType): - stmt.impl.func.type.arg_types[0] = class_type # Add an eq method, but only if the class doesn't already have one. if decorator_arguments['eq'] and info.get('__eq__') is None: diff --git a/mypy/semanal.py b/mypy/semanal.py index c40572a702cf..09b1fc22bb74 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -68,7 +68,7 @@ from mypy.types import ( FunctionLike, UnboundType, TypeVarDef, TupleType, UnionType, StarType, function_type, CallableType, Overloaded, Instance, Type, AnyType, - TypeTranslator, TypeOfAny + TypeTranslator, TypeOfAny, TypeType, ) from mypy.nodes import implicit_module_attrs from mypy.typeanal import ( @@ -479,10 +479,9 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo) -> None: elif isinstance(functype, CallableType): self_type = functype.arg_types[0] if isinstance(self_type, AnyType): + leading_type = fill_typevars(info) # type: Type if func.is_class or func.name() in ('__new__', '__init_subclass__'): - leading_type = self.class_type(info) - else: - leading_type = fill_typevars(info) + leading_type = self.class_type(leading_type) func.type = replace_implicit_first_type(functype, leading_type) def set_original_def(self, previous: Optional[Node], new: FuncDef) -> bool: @@ -775,8 +774,6 @@ def analyze_class_body(self, defn: ClassDef) -> Iterator[bool]: # that were already set in build_namedtuple_typeinfo. nt_names = named_tuple_info.names named_tuple_info.names = SymbolTable() - # This is needed for the cls argument to classmethods to get bound correctly. - named_tuple_info.names['__new__'] = nt_names['__new__'] self.enter_class(named_tuple_info) @@ -1343,15 +1340,8 @@ def object_type(self) -> Instance: def str_type(self) -> Instance: return self.named_type('__builtins__.str') - def class_type(self, info: TypeInfo) -> Type: - # Construct a function type whose fallback is cls. - from mypy import checkmember # To avoid import cycle. - leading_type = checkmember.type_object_type(info, self.builtin_type) - if isinstance(leading_type, Overloaded): - # Overloaded __init__ is too complex to handle. Plus it's stubs only. - return AnyType(TypeOfAny.special_form) - else: - return leading_type + def class_type(self, self_type: Type) -> Type: + return TypeType.make_normalized(self_type) def named_type(self, qualified_name: str, args: Optional[List[Type]] = None) -> Instance: sym = self.lookup_qualified(qualified_name, Context()) diff --git a/mypy/types.py b/mypy/types.py index 0f7c3113a208..acad55a12d75 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -706,7 +706,6 @@ def __init__(self, column: int = -1, is_ellipsis_args: bool = False, implicit: bool = False, - is_classmethod_class: bool = False, special_sig: Optional[str] = None, from_type_type: bool = False, bound_args: Sequence[Optional[Type]] = (), @@ -730,7 +729,6 @@ def __init__(self, self.variables = variables self.is_ellipsis_args = is_ellipsis_args self.implicit = implicit - self.is_classmethod_class = is_classmethod_class self.special_sig = special_sig self.from_type_type = from_type_type if not bound_args: @@ -780,7 +778,6 @@ def copy_modified(self, is_ellipsis_args=( is_ellipsis_args if is_ellipsis_args is not _dummy else self.is_ellipsis_args), implicit=implicit if implicit is not _dummy else self.implicit, - is_classmethod_class=self.is_classmethod_class, special_sig=special_sig if special_sig is not _dummy else self.special_sig, from_type_type=from_type_type if from_type_type is not _dummy else self.from_type_type, bound_args=bound_args if bound_args is not _dummy else self.bound_args, @@ -816,9 +813,6 @@ def is_kw_arg(self) -> bool: def is_type_obj(self) -> bool: return self.fallback.type.is_metaclass() - def is_concrete_type_obj(self) -> bool: - return self.is_type_obj() and self.is_classmethod_class - def type_object(self) -> mypy.nodes.TypeInfo: assert self.is_type_obj() ret = self.ret_type @@ -990,7 +984,6 @@ def serialize(self) -> JsonDict: 'variables': [v.serialize() for v in self.variables], 'is_ellipsis_args': self.is_ellipsis_args, 'implicit': self.implicit, - 'is_classmethod_class': self.is_classmethod_class, 'bound_args': [(None if t is None else t.serialize()) for t in self.bound_args], 'def_extras': dict(self.def_extras), @@ -1009,7 +1002,6 @@ def deserialize(cls, data: JsonDict) -> 'CallableType': variables=[TypeVarDef.deserialize(v) for v in data['variables']], is_ellipsis_args=data['is_ellipsis_args'], implicit=data['implicit'], - is_classmethod_class=data['is_classmethod_class'], bound_args=[(None if t is None else deserialize_type(t)) for t in data['bound_args']], def_extras=data['def_extras'] diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index 4a780e536ada..7f1c4fd889a7 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -367,6 +367,19 @@ A([1], '2') # E: Cannot infer type argument 1 of "A" [builtins fixtures/list.pyi] +[case testAttrsGenericClassmethod] +from typing import TypeVar, Generic, Optional +import attr +T = TypeVar('T') +@attr.s(auto_attribs=True) +class A(Generic[T]): + x: Optional[T] + @classmethod + def clsmeth(cls) -> None: + reveal_type(cls) # E: Revealed type is 'Type[__main__.A[T`1]]' + +[builtins fixtures/classmethod.pyi] + [case testAttrsForwardReference] import attr @attr.s(auto_attribs=True) @@ -416,7 +429,7 @@ class A: b: str = attr.ib() @classmethod def new(cls) -> A: - reveal_type(cls) # E: Revealed type is 'def (a: builtins.int, b: builtins.str) -> __main__.A' + reveal_type(cls) # E: Revealed type is 'Type[__main__.A]' return cls(6, 'hello') @classmethod def bad(cls) -> A: @@ -451,7 +464,7 @@ class A: @classmethod def foo(cls, x: Union[int, str]) -> Union[int, str]: - reveal_type(cls) # E: Revealed type is 'def (a: Any, b: Any =) -> __main__.A' + reveal_type(cls) # E: Revealed type is 'Type[__main__.A]' reveal_type(cls.other()) # E: Revealed type is 'builtins.str' return x diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index 990cba4e6f01..9e91a5c098ab 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -388,7 +388,7 @@ def f(a: Type[N]): a() [builtins fixtures/list.pyi] [out] -main:8: error: Unsupported type Type["N"] +main:9: error: Too few arguments for "N" [case testNewNamedTupleWithDefaults] # flags: --fast-parser --python-version 3.6 @@ -587,7 +587,7 @@ class XMethBad(NamedTuple): class MagicalFields(NamedTuple): x: int def __slots__(self) -> None: pass # E: Cannot overwrite NamedTuple attribute "__slots__" - def __new__(cls) -> None: pass # E: Name '__new__' already defined (possibly by an import) + def __new__(cls) -> None: pass # E: Cannot overwrite NamedTuple attribute "__new__" def _source(self) -> int: pass # E: Cannot overwrite NamedTuple attribute "_source" __annotations__ = {'x': float} # E: NamedTuple field name cannot start with an underscore: __annotations__ \ # E: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" \ @@ -640,7 +640,7 @@ class HasClassMethod(NamedTuple): @classmethod def new(cls, f: str) -> 'HasClassMethod': - reveal_type(cls) # E: Revealed type is 'def (x: builtins.str) -> Tuple[builtins.str, fallback=__main__.HasClassMethod]' + reveal_type(cls) # E: Revealed type is 'Type[Tuple[builtins.str, fallback=__main__.HasClassMethod]]' reveal_type(HasClassMethod) # E: Revealed type is 'def (x: builtins.str) -> Tuple[builtins.str, fallback=__main__.HasClassMethod]' return cls(x=f) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 16b80dd8433d..3e40c78be47b 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -339,6 +339,23 @@ class B(A): def __new__(cls) -> int: return 1 +[case testOverride__new__AndCallObject] +from typing import TypeVar, Generic + +class A: + def __new__(cls, x: int) -> 'A': + return object.__new__(cls) + +T = TypeVar('T') +class B(Generic[T]): + def __new__(cls, foo: T) -> 'B[T]': + x = object.__new__(cls) + # object.__new__ doesn't have a great type :( + reveal_type(x) # E: Revealed type is 'Any' + return x + +[builtins fixtures/__new__.pyi] + [case testInnerFunctionNotOverriding] class A: def f(self) -> int: pass @@ -3001,7 +3018,7 @@ def f(a: Type[N]): a() [builtins fixtures/list.pyi] [out] -main:3: error: Unsupported type Type["N"] +main:4: error: Too few arguments for "N" [case testTypeUsingTypeCJoin] from typing import Type @@ -5118,6 +5135,46 @@ class C: [builtins fixtures/property.pyi] [out] +[case testClassMethodBeforeInit1] +class Foo: + @classmethod + def bar(cls) -> Foo: + return cls("bar") + + def __init__(self, baz: str) -> None: + self.baz = baz +[builtins fixtures/classmethod.pyi] + +[case testClassMethodBeforeInit2] +class Foo: + @classmethod + def bar(cls) -> Foo: + return cls(Bar()) + + def __init__(self, baz: 'Bar') -> None: + self.baz = baz + +class Bar: pass +[builtins fixtures/classmethod.pyi] + +[case testClassMethodBeforeInit3] +from typing import overload +class Foo: + @classmethod + @overload + def bar(cls, x: int) -> Foo: ... + @classmethod + @overload + def bar(cls, x: str) -> Foo: ... + @classmethod + def bar(cls, x: object) -> Foo: + return cls(x) + + def __init__(self, baz: object) -> None: + self.baz = baz + +[builtins fixtures/classmethod.pyi] + [case testNewAndInit1] class A: def __init__(self, x: int) -> None: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 7723c7c81a05..8d1d9803b705 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -249,7 +249,7 @@ class A: @classmethod def foo(cls, x: Union[int, str]) -> Union[int, str]: - reveal_type(cls) # E: Revealed type is 'def (a: builtins.int, b: builtins.str) -> __main__.A' + reveal_type(cls) # E: Revealed type is 'Type[__main__.A]' reveal_type(cls.other()) # E: Revealed type is 'builtins.str' return x @@ -421,6 +421,25 @@ s: str = a.bar() # E: Incompatible types in assignment (expression has type "in [builtins fixtures/list.pyi] +[case testDataclassGenericsClassmethod] +# flags: --python-version 3.6 +from dataclasses import dataclass +from typing import Generic, TypeVar + +T = TypeVar('T') + +@dataclass +class A(Generic[T]): + x: T + + @classmethod + def foo(cls) -> None: + reveal_type(cls) # E: Revealed type is 'Type[__main__.A[T`1]]' + reveal_type(cls(1)) # E: Revealed type is '__main__.A[builtins.int*]' + reveal_type(cls('wooooo')) # E: Revealed type is '__main__.A[builtins.str*]' + +[builtins fixtures/classmethod.pyi] + [case testDataclassesForwardRefs] from dataclasses import dataclass @@ -466,3 +485,18 @@ app.rating app.database_name # E: "SpecializedApplication" has no attribute "database_name" [builtins fixtures/list.pyi] + +[case testDataclassFactory] +from typing import Type, TypeVar +from dataclasses import dataclass + +T = TypeVar('T', bound='A') + +@dataclass +class A: + @classmethod + def make(cls: Type[T]) -> T: + reveal_type(cls) # E: Revealed type is 'Type[T`-1]' + reveal_type(cls()) # E: Revealed type is 'T`-1' + return cls() +[builtins fixtures/classmethod.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index ac56d09329ae..3f0bedbcf0cc 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -4072,7 +4072,7 @@ class Wrapper: @classmethod # E: Overloaded function implementation cannot produce return type of signature 1 def foo(cls, x: Union[int, str]) -> str: - reveal_type(cls) # E: Revealed type is 'def () -> __main__.Wrapper' + reveal_type(cls) # E: Revealed type is 'Type[__main__.Wrapper]' reveal_type(cls.other()) # E: Revealed type is 'builtins.str' return "..." diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 4d79b9272026..1c12048982f9 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -345,11 +345,11 @@ class D: class E: def __new__(cls) -> E: - reveal_type(cls) # E: Revealed type is 'def () -> __main__.E' + reveal_type(cls) # E: Revealed type is 'Type[__main__.E]' return cls() def __init_subclass__(cls) -> None: - reveal_type(cls) # E: Revealed type is 'def () -> __main__.E' + reveal_type(cls) # E: Revealed type is 'Type[__main__.E]' [case testSelfTypePropertyUnion] from typing import Union diff --git a/test-data/unit/fixtures/__new__.pyi b/test-data/unit/fixtures/__new__.pyi index 4e2cc5771209..7e31ee05bce4 100644 --- a/test-data/unit/fixtures/__new__.pyi +++ b/test-data/unit/fixtures/__new__.pyi @@ -1,9 +1,11 @@ # builtins stub with object.__new__ +from typing import Any + class object: def __init__(self) -> None: pass - def __new__(cls): pass + def __new__(cls) -> Any: pass class type: def __init__(self, x) -> None: pass diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index 50a0bc332f64..f120a6adaeed 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -470,7 +470,7 @@ MypyFile:1( Args( Var(cls) Var(z)) - def (cls: def () -> __main__.A, z: builtins.int) -> builtins.str + def (cls: Type[__main__.A], z: builtins.int) -> builtins.str Class Block:3( PassStmt:3()))))) @@ -490,7 +490,7 @@ MypyFile:1( f Args( Var(cls)) - def (cls: def () -> __main__.A) -> builtins.str + def (cls: Type[__main__.A]) -> builtins.str Class Block:3( PassStmt:3())))))