Skip to content

Add support for __init_subclass__ #2654

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 3 commits into from
Jan 9, 2017
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: 3 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,10 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: str) -> None:

if fdef:
# Check if __init__ has an invalid, non-None return type.
if (fdef.info and fdef.name() == '__init__' and
if (fdef.info and fdef.name() in ('__init__', '__init_subclass__') and
not isinstance(typ.ret_type, (Void, NoneTyp)) and
not self.dynamic_funcs[-1]):
self.fail(messages.INIT_MUST_HAVE_NONE_RETURN_TYPE,
self.fail(messages.MUST_HAVE_NONE_RETURN_TYPE.format(fdef.name()),
item.type)

show_untyped = not self.is_typeshed_stub or self.options.warn_incomplete_stub
Expand Down Expand Up @@ -618,7 +618,7 @@ def is_implicit_any(t: Type) -> bool:
if (isinstance(defn, FuncDef) and ref_type is not None and i == 0
and not defn.is_static
and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]):
if defn.is_class or defn.name() == '__new__':
if defn.is_class or defn.name() in ('__new__', '__init_subclass__'):
ref_type = mypy.types.TypeType(ref_type)
erased = erase_to_bound(arg_type)
if not is_subtype_ignoring_tvars(ref_type, erased):
Expand Down
2 changes: 1 addition & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
INCOMPATIBLE_TYPES_IN_YIELD = 'Incompatible types in yield'
INCOMPATIBLE_TYPES_IN_YIELD_FROM = 'Incompatible types in "yield from"'
INCOMPATIBLE_TYPES_IN_STR_INTERPOLATION = 'Incompatible types in string interpolation'
INIT_MUST_HAVE_NONE_RETURN_TYPE = 'The return type of "__init__" must be None'
MUST_HAVE_NONE_RETURN_TYPE = 'The return type of "{}" must be None'
TUPLE_INDEX_MUST_BE_AN_INT_LITERAL = 'Tuple index must be an integer literal'
TUPLE_SLICE_MUST_BE_AN_INT_LITERAL = 'Tuple slice must be an integer literal'
TUPLE_INDEX_OUT_OF_RANGE = 'Tuple index out of range'
Expand Down
2 changes: 1 addition & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def prepare_method_signature(self, func: FuncDef) -> None:
elif isinstance(functype, CallableType):
self_type = functype.arg_types[0]
if isinstance(self_type, AnyType):
if func.is_class or func.name() == '__new__':
if func.is_class or func.name() in ('__new__', '__init_subclass__'):
leading_type = self.class_type(self.type)
else:
leading_type = fill_typevars(self.type)
Expand Down
2 changes: 2 additions & 0 deletions mypy/sharedparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"__imod__",
"__imul__",
"__init__",
"__init_subclass__",
"__int__",
"__invert__",
"__ior__",
Expand Down Expand Up @@ -84,6 +85,7 @@

MAGIC_METHODS_ALLOWING_KWARGS = {
"__init__",
"__init_subclass__",
"__new__",
}

Expand Down
16 changes: 15 additions & 1 deletion test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,20 @@ class A:
[out]
main:3: error: The return type of "__init__" must be None

[case testInitSubclassWithReturnValueType]
import typing
class A:
def __init_subclass__(cls) -> 'A': pass
[out]
main:3: error: The return type of "__init_subclass__" must be None

[case testInitSubclassWithImplicitReturnValueType]
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 testGlobalFunctionInitWithReturnType]
import typing
a = __init__() # type: A
Expand Down Expand Up @@ -1205,7 +1219,7 @@ class D:
def __get__(self, inst: Any, own: str) -> Any: pass
class A:
f = D()
A().f # E: Argument 2 to "__get__" of "D" has incompatible type Type[A]; expected "str"
A().f # E: Argument 2 to "__get__" of "D" has incompatible type Type[A]; expected "str"

[case testDescriptorGetSetDifferentTypes]
from typing import Any
Expand Down
43 changes: 29 additions & 14 deletions test-data/unit/check-selftype.test
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ reveal_type(B.new()) # E: Revealed type is '__main__.B*'

[case testSelfTypeOverride]
from typing import TypeVar, cast

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next time you submit a PR, can you instruct your editor not to clean up trailing whitespace on lines you otherwise didn't touch? While it looks better, the many trivial chunks in the diff are distracting during a review.

T = TypeVar('T', bound='A', covariant=True)

class A:
def copy(self: T) -> T: pass

Expand All @@ -124,15 +124,15 @@ class C(A):
reveal_type(C().copy) # E: Revealed type is 'def () -> __main__.C*'
reveal_type(C().copy()) # E: Revealed type is '__main__.C*'
reveal_type(cast(A, C()).copy) # E: Revealed type is 'def () -> __main__.A*'
reveal_type(cast(A, C()).copy()) # E: Revealed type is '__main__.A*'
reveal_type(cast(A, C()).copy()) # E: Revealed type is '__main__.A*'

[builtins fixtures/bool.pyi]

[case testSelfTypeSuper]
from typing import TypeVar, cast

T = TypeVar('T', bound='A', covariant=True)

class A:
def copy(self: T) -> T: pass

Expand All @@ -147,7 +147,7 @@ class B(A):

[case testSelfTypeRecursiveBinding]
from typing import TypeVar, Callable, Type

T = TypeVar('T', bound='A', covariant=True)
class A:
# TODO: This is potentially unsafe, as we use T in an argument type
Expand All @@ -173,7 +173,7 @@ reveal_type(B.new) # E: Revealed type is 'def (factory: def (__main__.B*) -> __

[case testSelfTypeBound]
from typing import TypeVar, Callable, cast

TA = TypeVar('TA', bound='A', covariant=True)

class A:
Expand All @@ -200,9 +200,9 @@ class B(A):
-- # TODO: fail for this
-- [case testSelfTypeBare]
-- from typing import TypeVar, Type
--
--
-- T = TypeVar('T', bound='E')
--
--
-- class E:
-- def copy(self: T, other: T) -> T: pass

Expand Down Expand Up @@ -262,7 +262,7 @@ class B:
@classmethod
def cfoo(cls: Type[Q]) -> Q:
return cls()

class C:
def foo(self: C) -> C: return self

Expand All @@ -272,7 +272,7 @@ class C:

class D:
def foo(self: str) -> str: # E: The erased type of self 'builtins.str' is not a supertype of its class '__main__.D'
return self
return self

@staticmethod
def bar(self: str) -> str:
Expand Down Expand Up @@ -302,28 +302,43 @@ class C:
[case testSelfTypeNew]
from typing import TypeVar, Type

T = TypeVar('T', bound=A)
class A:
T = TypeVar('T', bound=A)
class A:
def __new__(cls: Type[T]) -> T:
return cls()

def __init_subclass__(cls: Type[T]) -> None:
pass

class B:
def __new__(cls: Type[T]) -> T: # E: The erased type of self 'Type[__main__.A]' is not a supertype of its class 'Type[__main__.B]'
return cls()

class C:
def __init_subclass__(cls: Type[T]) -> None: # E: The erased type of self 'Type[__main__.A]' is not a supertype of its class 'Type[__main__.B]'
pass

class C:
def __new__(cls: Type[C]) -> C:
return cls()

def __init_subclass__(cls: Type[C]) -> None:
pass

class D:
def __new__(cls: D) -> D: # E: The erased type of self '__main__.D' is not a supertype of its class 'Type[__main__.D]'
return cls

def __init_subclass__(cls: D) -> None: # E: The erased type of self '__main__.D' is not a supertype of its class 'Type[__main__.D]'
pass

class E:
def __new__(cls) -> E:
reveal_type(cls) # E: Revealed type is 'def () -> __main__.E'
return cls()

def __init_subclass__(cls) -> None:
reveal_type(cls) # E: Revealed type is 'def () -> __main__.E'

[case testSelfTypeProperty]
from typing import TypeVar

Expand Down