Skip to content

Commit 97d25cf

Browse files
ilevkivskyimsullivan
authored andcommitted
Fix __init_subclass__ type check (python#7723)
1 parent 9751449 commit 97d25cf

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

mypy/checker.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,7 +1706,9 @@ def visit_class_def(self, defn: ClassDef) -> None:
17061706
with self.scope.push_class(defn.info):
17071707
self.accept(defn.defs)
17081708
self.binder = old_binder
1709-
self.check_init_subclass(defn)
1709+
if not (defn.info.typeddict_type or defn.info.tuple_type or defn.info.is_enum):
1710+
# If it is not a normal class (not a special form) check class keywords.
1711+
self.check_init_subclass(defn)
17101712
if not defn.has_incompatible_baseclass:
17111713
# Otherwise we've already found errors; more errors are not useful
17121714
self.check_multiple_inheritance(typ)
@@ -1751,6 +1753,11 @@ def check_init_subclass(self, defn: ClassDef) -> None:
17511753
Base.__init_subclass__(thing=5) is called at line 4. This is what we simulate here.
17521754
Child.__init_subclass__ is never called.
17531755
"""
1756+
if (defn.info.metaclass_type and
1757+
defn.info.metaclass_type.type.fullname() not in ('builtins.type', 'abc.ABCMeta')):
1758+
# We can't safely check situations when both __init_subclass__ and a custom
1759+
# metaclass are present.
1760+
return
17541761
# At runtime, only Base.__init_subclass__ will be called, so
17551762
# we skip the current class itself.
17561763
for base in defn.info.mro[1:]:
@@ -1775,10 +1782,9 @@ def check_init_subclass(self, defn: ClassDef) -> None:
17751782
self.expr_checker.accept(call_expr,
17761783
allow_none_return=True,
17771784
always_allow_any=True)
1778-
# We are only interested in the first Base having __init_subclass__
1785+
# We are only interested in the first Base having __init_subclass__,
17791786
# all other bases have already been checked.
17801787
break
1781-
return
17821788

17831789
def check_protocol_variance(self, defn: ClassDef) -> None:
17841790
"""Check that protocol definition is compatible with declared

test-data/unit/check-classes.test

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6229,14 +6229,21 @@ class Child(Base, thing=5, default_name=""):
62296229
[builtins fixtures/object_with_init_subclass.pyi]
62306230

62316231
[case testInitSubclassWithMetaclassOK]
6232-
class Base(type):
6232+
class Base:
62336233
thing: int
62346234

62356235
def __init_subclass__(cls, thing: int):
62366236
cls.thing = thing
62376237

6238-
class Child(Base, metaclass=Base, thing=0):
6238+
class Child(Base, metaclass=type, thing=0):
62396239
pass
6240+
[builtins fixtures/object_with_init_subclass.pyi]
6241+
6242+
[case testInitSubclassWithCustomMetaclassOK]
6243+
class M(type): ...
6244+
class Child(metaclass=M, thing=0):
6245+
pass
6246+
[builtins fixtures/object_with_init_subclass.pyi]
62406247

62416248
[case testTooManyArgsForObject]
62426249
class A(thing=5):

test-data/unit/fixtures/dict.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ VT = TypeVar('VT')
1010

1111
class object:
1212
def __init__(self) -> None: pass
13+
def __init_subclass__(cls) -> None: pass
1314
def __eq__(self, other: object) -> bool: pass
1415

1516
class type: pass

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy