Skip to content

Commit 043cd60

Browse files
bpo-44806: Fix __init__ in subclasses of protocols (GH-27545)
Non-protocol subclasses of protocol ignore now the __init__ method inherited from protocol base classes.
1 parent 36d952d commit 043cd60

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

Diff for: Lib/test/test_typing.py

+36
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,9 @@ class P(Protocol): pass
881881
class C(P): pass
882882

883883
self.assertIsInstance(C(), C)
884+
with self.assertRaises(TypeError):
885+
C(42)
886+
884887
T = TypeVar('T')
885888

886889
class PG(Protocol[T]): pass
@@ -895,6 +898,8 @@ class PG(Protocol[T]): pass
895898
class CG(PG[T]): pass
896899

897900
self.assertIsInstance(CG[int](), CG)
901+
with self.assertRaises(TypeError):
902+
CG[int](42)
898903

899904
def test_cannot_instantiate_abstract(self):
900905
@runtime_checkable
@@ -1322,6 +1327,37 @@ def __init__(self):
13221327

13231328
self.assertEqual(C[int]().test, 'OK')
13241329

1330+
class B:
1331+
def __init__(self):
1332+
self.test = 'OK'
1333+
1334+
class D1(B, P[T]):
1335+
pass
1336+
1337+
self.assertEqual(D1[int]().test, 'OK')
1338+
1339+
class D2(P[T], B):
1340+
pass
1341+
1342+
self.assertEqual(D2[int]().test, 'OK')
1343+
1344+
def test_new_called(self):
1345+
T = TypeVar('T')
1346+
1347+
class P(Protocol[T]): pass
1348+
1349+
class C(P[T]):
1350+
def __new__(cls, *args):
1351+
self = super().__new__(cls, *args)
1352+
self.test = 'OK'
1353+
return self
1354+
1355+
self.assertEqual(C[int]().test, 'OK')
1356+
with self.assertRaises(TypeError):
1357+
C[int](42)
1358+
with self.assertRaises(TypeError):
1359+
C[int](a=42)
1360+
13251361
def test_protocols_bad_subscripts(self):
13261362
T = TypeVar('T')
13271363
S = TypeVar('S')

Diff for: Lib/typing.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1381,8 +1381,7 @@ def _is_callable_members_only(cls):
13811381

13821382

13831383
def _no_init(self, *args, **kwargs):
1384-
if type(self)._is_protocol:
1385-
raise TypeError('Protocols cannot be instantiated')
1384+
raise TypeError('Protocols cannot be instantiated')
13861385

13871386
def _caller(depth=1, default='__main__'):
13881387
try:
@@ -1522,6 +1521,15 @@ def _proto_hook(other):
15221521

15231522
# We have nothing more to do for non-protocols...
15241523
if not cls._is_protocol:
1524+
if cls.__init__ == _no_init:
1525+
for base in cls.__mro__:
1526+
init = base.__dict__.get('__init__', _no_init)
1527+
if init != _no_init:
1528+
cls.__init__ = init
1529+
break
1530+
else:
1531+
# should not happen
1532+
cls.__init__ = object.__init__
15251533
return
15261534

15271535
# ... otherwise check consistency of bases, and prohibit instantiation.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Non-protocol subclasses of :class:`typing.Protocol` ignore now the
2+
``__init__`` method inherited from protocol base classes.

0 commit comments

Comments
 (0)