Skip to content

Commit 99506dc

Browse files
[3.9] bpo-45121: Fix RecursionError when calling Protocol.__init__ from a subclass' __init__ (GH-28206) (GH-28233)
Co-authored-by: Yurii Karabas <[email protected]>
1 parent d9b7d42 commit 99506dc

File tree

3 files changed

+18
-0
lines changed

3 files changed

+18
-0
lines changed

Lib/test/test_typing.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,17 @@ class CustomProtocol(TestCase, Protocol):
14591459
class CustomContextManager(typing.ContextManager, Protocol):
14601460
pass
14611461

1462+
def test_super_call_init(self):
1463+
class P(Protocol):
1464+
x: int
1465+
1466+
class Foo(P):
1467+
def __init__(self):
1468+
super().__init__()
1469+
1470+
Foo() # Previously triggered RecursionError
1471+
1472+
14621473
class GenericTests(BaseTestCase):
14631474

14641475
def test_basics(self):

Lib/typing.py

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

1088+
# Already using a custom `__init__`. No need to calculate correct
1089+
# `__init__` to call. This can lead to RecursionError. See bpo-45121.
1090+
if cls.__init__ is not _no_init_or_replace_init:
1091+
return
1092+
10881093
# Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
10891094
# The first instantiation of the subclass will call `_no_init_or_replace_init` which
10901095
# 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)