From 85c173b81f3d0e7ce8935152851fbcb07cd8b603 Mon Sep 17 00:00:00 2001 From: Ekin Dursun Date: Mon, 1 Oct 2018 19:40:40 +0300 Subject: [PATCH] Make return type implicitly None for type checked __init__ and __init_subclass__ (#5677) Implements https://github.com/python/mypy/issues/604#issuecomment-348525995. --- mypy/semanal.py | 6 +- test-data/unit/check-classes.test | 113 +++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index a4174de03742..e94f0a56282c 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, TypeType, + TypeTranslator, TypeOfAny, TypeType, NoneTyp, ) from mypy.nodes import implicit_module_attrs from mypy.typeanal import ( @@ -407,6 +407,10 @@ def _visit_func_def(self, defn: FuncDef) -> None: add_symbol = False if add_symbol: self.type.names[defn.name()] = SymbolTableNode(MDEF, defn) + if defn.type is not None and defn.name() in ('__init__', '__init_subclass__'): + assert isinstance(defn.type, CallableType) + if isinstance(defn.type.ret_type, AnyType): + defn.type = defn.type.copy_modified(ret_type=NoneTyp()) self.prepare_method_signature(defn, self.type) elif self.is_func_scope(): # Nested function diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 3e40c78be47b..7784f26df4b2 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -577,7 +577,61 @@ import typing class A: def __init__(self, x: int): pass [out] -main:3: error: The return type of "__init__" must be None + +[case testDecoratedConstructorWithImplicitReturnValueType] +import typing +from typing import Callable + +def deco(fn: Callable) -> Callable: + return fn + +class A: + @deco + def __init__(self, x: int): pass +[out] + +[case testOverloadedConstructorWithImplicitReturnValueType] +from foo import * +[file foo.pyi] +from typing import overload +class Foo: + @overload + def __init__(self, a: int): + pass + + @overload + def __init__(self, a: str): + pass + +[case testConstructorWithAnyReturnValueType] +import typing +from typing import Any +class A: + def __init__(self) -> Any: pass # E: The return type of "__init__" must be None + +[case testDecoratedConstructorWithAnyReturnValueType] +import typing +from typing import Callable, Any + +def deco(fn: Callable) -> Callable: + return fn + +class A: + @deco + def __init__(self) -> Any: pass # E: The return type of "__init__" must be None + +[case testOverloadedConstructorWithAnyReturnValueType] +from foo import * +[file foo.pyi] +from typing import overload, Any +class Foo: + @overload + def __init__(self, a: int) -> Any: # E: The return type of "__init__" must be None + pass + + @overload + def __init__(self, a: str) -> Any: # E: The return type of "__init__" must be None + pass [case testInitSubclassWithReturnValueType] import typing @@ -591,7 +645,62 @@ import typing class A: def __init_subclass__(cls, x: int=1): pass [out] -main:3: error: The return type of "__init_subclass__" must be None + +[case testDecoratedInitSubclassWithImplicitReturnValueType] +import typing +from typing import Callable + +def deco(fn: Callable) -> Callable: + return fn + +class A: + @deco + def __init_subclass__(cls, x: int=1): pass +[out] + +[case testOverloadedInitSubclassWithImplicitReturnValueType] +from foo import * +[file foo.pyi] +from typing import overload +class Foo: + @overload + def __init_subclass__(cls, a: int): + pass + + @overload + def __init_subclass__(cls, a: str): + pass + +[case testInitSubclassWithAnyReturnValueType] +import typing +from typing import Any +class A: + def __init_subclass__(cls) -> Any: pass # E: The return type of "__init_subclass__" must be None + +[case testDecoratedInitSubclassWithAnyReturnValueType] +import typing +from typing import Callable, Any + +def deco(fn: Callable) -> Callable: + return fn + +class A: + @deco + def __init_subclass__(cls) -> Any: pass # E: The return type of "__init_subclass__" must be None +[out] + +[case testOverloadedInitSubclassWithAnyReturnValueType] +from foo import * +[file foo.pyi] +from typing import overload, Any +class Foo: + @overload + def __init_subclass__(cls, a: int) -> Any: # E: The return type of "__init_subclass__" must be None + pass + + @overload + def __init_subclass__(cls, a: str) -> Any: # E: The return type of "__init_subclass__" must be None + pass [case testGlobalFunctionInitWithReturnType] import typing