Skip to content
Open
Show file tree
Hide file tree
Changes from all 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.
- 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