Skip to content
Merged
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
21 changes: 20 additions & 1 deletion Lib/test/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import builtins
import unittest
from unittest.mock import Mock
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol
from typing import get_type_hints
from collections import deque, OrderedDict, namedtuple
from functools import total_ordering
Expand Down Expand Up @@ -2150,6 +2150,25 @@ def __init__(self, x):
self.x = 2 * x
self.assertEqual(C(5).x, 10)

def test_inherit_from_protocol(self):
# See bpo-45081.
Comment thread
uriyyo marked this conversation as resolved.

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)


class TestRepr(unittest.TestCase):
def test_repr(self):
Expand Down
29 changes: 19 additions & 10 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1401,7 +1401,25 @@ def _is_callable_members_only(cls):


def _no_init(self, *args, **kwargs):
Comment thread
Fidget-Spinner marked this conversation as resolved.
Outdated
raise TypeError('Protocols cannot be instantiated')
cls = type(self)

if cls._is_protocol:
raise TypeError('Protocols cannot be instantiated')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: this is the previous behavior.


# set correct __init__ method on a first initialization
Comment thread
uriyyo marked this conversation as resolved.
Outdated
# so all further initialization will call it directly
# see bpo-45081
for base in cls.__mro__:
init = base.__dict__.get('__init__', _no_init)
if init is not _no_init:
cls.__init__ = init
break
else:
# should not happen
cls.__init__ = object.__init__

cls.__init__(self, *args, **kwargs)


def _caller(depth=1, default='__main__'):
try:
Expand Down Expand Up @@ -1541,15 +1559,6 @@ def _proto_hook(other):

# We have nothing more to do for non-protocols...
if not cls._is_protocol:
if cls.__init__ == _no_init:
for base in cls.__mro__:
init = base.__dict__.get('__init__', _no_init)
if init != _no_init:
cls.__init__ = init
break
else:
# should not happen
cls.__init__ = object.__init__
return

# ... otherwise check consistency of bases, and prohibit instantiation.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix issue when dataclasses that inherit from ``typing.Protocol`` subclasses
have wrong ``__init__``. Patch provided by Yurii Karabas.