Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Fix `__init__` in subclasses of protocols.
Comment thread
JelleZijlstra marked this conversation as resolved.
Outdated
- Fix incorrect behaviour on Python 3.9 and Python 3.10 that meant that
calling `isinstance` with `typing_extensions.Concatenate[...]` or
`typing_extensions.Unpack[...]` as the first argument could have a different
Expand Down
48 changes: 48 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import typing
import warnings
from collections import defaultdict
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from unittest import TestCase, main, skipIf, skipUnless
Expand Down Expand Up @@ -3697,12 +3698,59 @@ def __init__(self, x: T) -> None:

def test_init_called(self):
T = TypeVar('T')

class P(Protocol[T]): pass

class C(P[T]):
def __init__(self):
self.test = 'OK'

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

class B:
def __init__(self):
self.test = 'OK'

class D1(B, P[T]):
pass

self.assertEqual(D1[int]().test, 'OK')

class D2(P[T], B):
pass

self.assertEqual(D2[int]().test, 'OK')

def test_super_call_init(self):
class P(Protocol):
x: int

class Foo(P):
def __init__(self):
super().__init__()

Foo() # Previously triggered RecursionError

def test_inherit_from_protocol(self):
# Dataclasses inheriting from protocol should preserve their own `__init__`.
# See bpo-45081.

class P(Protocol):
a: int

@dataclass
class C(P):
a: int

self.assertEqual(C(5).a, 5)

@dataclass
class D(P):
def __init__(self, a):
self.a = a * 2

self.assertEqual(D(5).a, 10)

def test_protocols_bad_subscripts(self):
T = TypeVar('T')
S = TypeVar('S')
Expand Down
6 changes: 1 addition & 5 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,10 +664,6 @@ def _allow_reckless_class_checks(depth=2):
"""
return _caller(depth) in {'abc', 'functools', None}

def _no_init(self, *args, **kwargs):
if type(self)._is_protocol:
raise TypeError('Protocols cannot be instantiated')

def _type_check_issubclass_arg_1(arg):
"""Raise TypeError if `arg` is not an instance of `type`
in `issubclass(arg, <protocol>)`.
Expand Down Expand Up @@ -831,7 +827,7 @@ def __init_subclass__(cls, *args, **kwargs):

# Prohibit instantiation for protocol classes
if cls._is_protocol and cls.__init__ is Protocol.__init__:
cls.__init__ = _no_init
cls.__init__ = typing._no_init_or_replace_init


# Breakpoint: https://github.com/python/cpython/pull/113401
Expand Down
Loading