Skip to content

Commit c11956a

Browse files
authored
bpo-45121: Fix RecursionError when calling Protocol.__init__ from a subclass' __init__ (GH-28206)
1 parent d003a5b commit c11956a

File tree

3 files changed

+17
-0
lines changed

3 files changed

+17
-0
lines changed

Lib/test/test_typing.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,16 @@ class P(Protocol):
16101610
with self.assertRaisesRegex(TypeError, "@runtime_checkable"):
16111611
isinstance(1, P)
16121612

1613+
def test_super_call_init(self):
1614+
class P(Protocol):
1615+
x: int
1616+
1617+
class Foo(P):
1618+
def __init__(self):
1619+
super().__init__()
1620+
1621+
Foo() # Previously triggered RecursionError
1622+
16131623

16141624
class GenericTests(BaseTestCase):
16151625

Lib/typing.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,11 @@ def _no_init_or_replace_init(self, *args, **kwargs):
14061406
if cls._is_protocol:
14071407
raise TypeError('Protocols cannot be instantiated')
14081408

1409+
# Already using a custom `__init__`. No need to calculate correct
1410+
# `__init__` to call. This can lead to RecursionError. See bpo-45121.
1411+
if cls.__init__ is not _no_init_or_replace_init:
1412+
return
1413+
14091414
# Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
14101415
# The first instantiation of the subclass will call `_no_init_or_replace_init` which
14111416
# searches for a proper new `__init__` in the MRO. The new `__init__`
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix issue where ``Protocol.__init__`` raises ``RecursionError`` when it's
2+
called directly or via ``super()``. Patch provided by Yurii Karabas.

0 commit comments

Comments
 (0)