Skip to content

Commit 222b14d

Browse files
ilevkivskyimsullivan
authored andcommitted
Fix __init_subclass__ type check (#7723)
1 parent f89099c commit 222b14d

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
@@ -6339,14 +6339,21 @@ class Child(Base, thing=5, default_name=""):
63396339
[builtins fixtures/object_with_init_subclass.pyi]
63406340

63416341
[case testInitSubclassWithMetaclassOK]
6342-
class Base(type):
6342+
class Base:
63436343
thing: int
63446344

63456345
def __init_subclass__(cls, thing: int):
63466346
cls.thing = thing
63476347

6348-
class Child(Base, metaclass=Base, thing=0):
6348+
class Child(Base, metaclass=type, thing=0):
63496349
pass
6350+
[builtins fixtures/object_with_init_subclass.pyi]
6351+
6352+
[case testInitSubclassWithCustomMetaclassOK]
6353+
class M(type): ...
6354+
class Child(metaclass=M, thing=0):
6355+
pass
6356+
[builtins fixtures/object_with_init_subclass.pyi]
63506357

63516358
[case testTooManyArgsForObject]
63526359
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