| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,7 +9,7 @@ | |||
| 9 | 9 | import builtins | |
| 10 | 10 | import unittest | |
| 11 | 11 | from unittest.mock import Mock | |
| 12 | - from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional | ||
| 12 | + from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol | ||
| 13 | 13 | from typing import get_type_hints | |
| 14 | 14 | from collections import deque, OrderedDict, namedtuple | |
| 15 | 15 | from functools import total_ordering | |
@@ -2124,6 +2124,26 @@ def __init__(self, x): | |||
| 2124 | 2124 | self.x = 2 * x | |
| 2125 | 2125 | self.assertEqual(C(5).x, 10) | |
| 2126 | 2126 | ||
| 2127 | + def test_inherit_from_protocol(self): | ||
| 2128 | + # Dataclasses inheriting from protocol should preserve their own `__init__`. | ||
| 2129 | + # See bpo-45081. | ||
| 2130 | + | ||
| 2131 | + class P(Protocol): | ||
| 2132 | + a: int | ||
| 2133 | + | ||
| 2134 | + @dataclass | ||
| 2135 | + class C(P): | ||
| 2136 | + a: int | ||
| 2137 | + | ||
| 2138 | + self.assertEqual(C(5).a, 5) | ||
| 2139 | + | ||
| 2140 | + @dataclass | ||
| 2141 | + class D(P): | ||
| 2142 | + def __init__(self, a): | ||
| 2143 | + self.a = a * 2 | ||
| 2144 | + | ||
| 2145 | + self.assertEqual(D(5).a, 10) | ||
| 2146 | + | ||
| 2127 | 2147 | ||
| 2128 | 2148 | class TestRepr(unittest.TestCase): | |
| 2129 | 2149 | def test_repr(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1079,8 +1079,29 @@ def _is_callable_members_only(cls): | |||
| 1079 | 1079 | return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) | |
| 1080 | 1080 | ||
| 1081 | 1081 | ||
| 1082 | - def _no_init(self, *args, **kwargs): | ||
| 1083 | - raise TypeError('Protocols cannot be instantiated') | ||
| 1082 | + def _no_init_or_replace_init(self, *args, **kwargs): | ||
| 1083 | + cls = type(self) | ||
| 1084 | + | ||
| 1085 | + if cls._is_protocol: | ||
| 1086 | + raise TypeError('Protocols cannot be instantiated') | ||
| 1087 | + | ||
| 1088 | + # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. | ||
| 1089 | + # The first instantiation of the subclass will call `_no_init_or_replace_init` which | ||
| 1090 | + # searches for a proper new `__init__` in the MRO. The new `__init__` | ||
| 1091 | + # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent | ||
| 1092 | + # instantiation of the protocol subclass will thus use the new | ||
| 1093 | + # `__init__` and no longer call `_no_init_or_replace_init`. | ||
| 1094 | + for base in cls.__mro__: | ||
| 1095 | + init = base.__dict__.get('__init__', _no_init_or_replace_init) | ||
| 1096 | + if init is not _no_init_or_replace_init: | ||
| 1097 | + cls.__init__ = init | ||
| 1098 | + break | ||
| 1099 | + else: | ||
| 1100 | + # should not happen | ||
| 1101 | + cls.__init__ = object.__init__ | ||
| 1102 | + | ||
| 1103 | + cls.__init__(self, *args, **kwargs) | ||
| 1104 | + | ||
| 1084 | 1105 | ||
| 1085 | 1106 | ||
| 1086 | 1107 | def _allow_reckless_class_cheks(): | |
@@ -1209,15 +1230,6 @@ def _proto_hook(other): | |||
| 1209 | 1230 | ||
| 1210 | 1231 | # We have nothing more to do for non-protocols... | |
| 1211 | 1232 | if not cls._is_protocol: | |
| 1212 | - if cls.__init__ == _no_init: | ||
| 1213 | - for base in cls.__mro__: | ||
| 1214 | - init = base.__dict__.get('__init__', _no_init) | ||
| 1215 | - if init != _no_init: | ||
| 1216 | - cls.__init__ = init | ||
| 1217 | - break | ||
| 1218 | - else: | ||
| 1219 | - # should not happen | ||
| 1220 | - cls.__init__ = object.__init__ | ||
| 1221 | 1233 | return | |
| 1222 | 1234 | ||
| 1223 | 1235 | # ... otherwise check consistency of bases, and prohibit instantiation. | |
@@ -1228,7 +1240,7 @@ def _proto_hook(other): | |||
| 1228 | 1240 | issubclass(base, Generic) and base._is_protocol): | |
| 1229 | 1241 | raise TypeError('Protocols can only inherit from other' | |
| 1230 | 1242 | ' protocols, got %r' % base) | |
| 1231 | - cls.__init__ = _no_init | ||
| 1243 | + cls.__init__ = _no_init_or_replace_init | ||
| 1232 | 1244 | ||
| 1233 | 1245 | ||
| 1234 | 1246 | class _AnnotatedAlias(_GenericAlias, _root=True): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Fix issue when dataclasses that inherit from ``typing.Protocol`` subclasses | ||
| 2 | + have wrong ``__init__``. Patch provided by Yurii Karabas. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments