| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4c81bd1 commit 039b433
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | # Release 4.x.x | |
| 2 | 2 | ||
| 3 | + - Add `Never` and `assert_never`. Backport from bpo-46475. | ||
| 3 | 4 | - `ParamSpec` args and kwargs are now equal to themselves. Backport from | |
| 4 | 5 | bpo-46676. Patch by Gregory Beauregard (@GBeauregard). | |
| 5 | 6 | - Add `reveal_type`. Backport from bpo-46414. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -43,6 +43,8 @@ This module currently contains the following: | |||
| 43 | 43 | ||
| 44 | 44 | - In ``typing`` since Python 3.11 | |
| 45 | 45 | ||
| 46 | + - ``assert_never`` | ||
| 47 | + - ``Never`` | ||
| 46 | 48 | - ``reveal_type`` | |
| 47 | 49 | - ``Self`` (see PEP 673) | |
| 48 | 50 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,7 +12,7 @@ | |||
| 12 | 12 | from unittest import TestCase, main, skipUnless, skipIf | |
| 13 | 13 | from test import ann_module, ann_module2, ann_module3 | |
| 14 | 14 | import typing | |
| 15 | - from typing import TypeVar, Optional, Union | ||
| 15 | + from typing import TypeVar, Optional, Union, Any | ||
| 16 | 16 | from typing import T, KT, VT # Not in __all__. | |
| 17 | 17 | from typing import Tuple, List, Dict, Iterable, Iterator, Callable | |
| 18 | 18 | from typing import Generic, NamedTuple | |
@@ -22,7 +22,7 @@ | |||
| 22 | 22 | from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard | |
| 23 | 23 | from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired | |
| 24 | 24 | from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, final, is_typeddict | |
| 25 | - from typing_extensions import dataclass_transform, reveal_type | ||
| 25 | + from typing_extensions import dataclass_transform, reveal_type, Never, assert_never | ||
| 26 | 26 | try: | |
| 27 | 27 | from typing_extensions import get_type_hints | |
| 28 | 28 | except ImportError: | |
@@ -70,43 +70,94 @@ class Employee: | |||
| 70 | 70 | pass | |
| 71 | 71 | ||
| 72 | 72 | ||
| 73 | - class NoReturnTests(BaseTestCase): | ||
| 73 | + class BottomTypeTestsMixin: | ||
| 74 | + bottom_type: ClassVar[Any] | ||
| 74 | 75 | ||
| 75 | - def test_noreturn_instance_type_error(self): | ||
| 76 | - with self.assertRaises(TypeError): | ||
| 77 | - isinstance(42, NoReturn) | ||
| 76 | + def test_equality(self): | ||
| 77 | + self.assertEqual(self.bottom_type, self.bottom_type) | ||
| 78 | + self.assertIs(self.bottom_type, self.bottom_type) | ||
| 79 | + self.assertNotEqual(self.bottom_type, None) | ||
| 78 | 80 | ||
| 79 | - def test_noreturn_subclass_type_error_1(self): | ||
| 80 | - with self.assertRaises(TypeError): | ||
| 81 | - issubclass(Employee, NoReturn) | ||
| 81 | + @skipUnless(PEP_560, "Python 3.7+ required") | ||
| 82 | + def test_get_origin(self): | ||
| 83 | + from typing_extensions import get_origin | ||
| 84 | + self.assertIs(get_origin(self.bottom_type), None) | ||
| 82 | 85 | ||
| 83 | - def test_noreturn_subclass_type_error_2(self): | ||
| 86 | + def test_instance_type_error(self): | ||
| 84 | 87 | with self.assertRaises(TypeError): | |
| 85 | - issubclass(NoReturn, Employee) | ||
| 88 | + isinstance(42, self.bottom_type) | ||
| 86 | 89 | ||
| 87 | - def test_repr(self): | ||
| 88 | - if hasattr(typing, 'NoReturn'): | ||
| 89 | - self.assertEqual(repr(NoReturn), 'typing.NoReturn') | ||
| 90 | - else: | ||
| 91 | - self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') | ||
| 90 | + def test_subclass_type_error(self): | ||
| 91 | + with self.assertRaises(TypeError): | ||
| 92 | + issubclass(Employee, self.bottom_type) | ||
| 93 | + with self.assertRaises(TypeError): | ||
| 94 | + issubclass(NoReturn, self.bottom_type) | ||
| 92 | 95 | ||
| 93 | 96 | def test_not_generic(self): | |
| 94 | 97 | with self.assertRaises(TypeError): | |
| 95 | - NoReturn[int] | ||
| 98 | + self.bottom_type[int] | ||
| 96 | 99 | ||
| 97 | 100 | def test_cannot_subclass(self): | |
| 98 | 101 | with self.assertRaises(TypeError): | |
| 99 | - class A(NoReturn): | ||
| 102 | + class A(self.bottom_type): | ||
| 100 | 103 | pass | |
| 101 | 104 | with self.assertRaises(TypeError): | |
| 102 | - class A(type(NoReturn)): | ||
| 105 | + class A(type(self.bottom_type)): | ||
| 103 | 106 | pass | |
| 104 | 107 | ||
| 105 | 108 | def test_cannot_instantiate(self): | |
| 106 | 109 | with self.assertRaises(TypeError): | |
| 107 | - NoReturn() | ||
| 110 | + self.bottom_type() | ||
| 108 | 111 | with self.assertRaises(TypeError): | |
| 109 | - type(NoReturn)() | ||
| 112 | + type(self.bottom_type)() | ||
| 113 | + | ||
| 114 | + | ||
| 115 | + class NoReturnTests(BottomTypeTestsMixin, BaseTestCase): | ||
| 116 | + bottom_type = NoReturn | ||
| 117 | + | ||
| 118 | + def test_repr(self): | ||
| 119 | + if hasattr(typing, 'NoReturn'): | ||
| 120 | + self.assertEqual(repr(NoReturn), 'typing.NoReturn') | ||
| 121 | + else: | ||
| 122 | + self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') | ||
| 123 | + | ||
| 124 | + def test_get_type_hints(self): | ||
| 125 | + def some(arg: NoReturn) -> NoReturn: ... | ||
| 126 | + def some_str(arg: 'NoReturn') -> 'typing.NoReturn': ... | ||
| 127 | + | ||
| 128 | + expected = {'arg': NoReturn, 'return': NoReturn} | ||
| 129 | + for target in [some, some_str]: | ||
| 130 | + with self.subTest(target=target): | ||
| 131 | + self.assertEqual(gth(target), expected) | ||
| 132 | + | ||
| 133 | + def test_not_equality(self): | ||
| 134 | + self.assertNotEqual(NoReturn, Never) | ||
| 135 | + self.assertNotEqual(Never, NoReturn) | ||
| 136 | + | ||
| 137 | + | ||
| 138 | + class NeverTests(BottomTypeTestsMixin, BaseTestCase): | ||
| 139 | + bottom_type = Never | ||
| 140 | + | ||
| 141 | + def test_repr(self): | ||
| 142 | + if hasattr(typing, 'Never'): | ||
| 143 | + self.assertEqual(repr(Never), 'typing.Never') | ||
| 144 | + else: | ||
| 145 | + self.assertEqual(repr(Never), 'typing_extensions.Never') | ||
| 146 | + | ||
| 147 | + def test_get_type_hints(self): | ||
| 148 | + def some(arg: Never) -> Never: ... | ||
| 149 | + def some_str(arg: 'Never') -> 'typing_extensions.Never': ... | ||
| 150 | + | ||
| 151 | + expected = {'arg': Never, 'return': Never} | ||
| 152 | + for target in [some, some_str]: | ||
| 153 | + with self.subTest(target=target): | ||
| 154 | + self.assertEqual(gth(target), expected) | ||
| 155 | + | ||
| 156 | + | ||
| 157 | + class AssertNeverTests(BaseTestCase): | ||
| 158 | + def test_exception(self): | ||
| 159 | + with self.assertRaises(AssertionError): | ||
| 160 | + assert_never(None) | ||
| 110 | 161 | ||
| 111 | 162 | ||
| 112 | 163 | class ClassVarTests(BaseTestCase): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,6 +70,7 @@ def _check_generic(cls, parameters): | |||
| 70 | 70 | ||
| 71 | 71 | # One-off things. | |
| 72 | 72 | 'Annotated', | |
| 73 | + 'assert_never', | ||
| 73 | 74 | 'dataclass_transform', | |
| 74 | 75 | 'final', | |
| 75 | 76 | 'IntVar', | |
@@ -85,6 +86,7 @@ def _check_generic(cls, parameters): | |||
| 85 | 86 | 'TypeAlias', | |
| 86 | 87 | 'TypeGuard', | |
| 87 | 88 | 'TYPE_CHECKING', | |
| 89 | + 'Never', | ||
| 88 | 90 | 'NoReturn', | |
| 89 | 91 | 'Required', | |
| 90 | 92 | 'NotRequired', | |
@@ -2107,9 +2109,8 @@ def __eq__(self, other): | |||
| 2107 | 2109 | ||
| 2108 | 2110 | TypeGuard = _TypeGuard(_root=True) | |
| 2109 | 2111 | ||
| 2110 | - if hasattr(typing, "Self"): | ||
| 2111 | - Self = typing.Self | ||
| 2112 | - elif sys.version_info[:2] >= (3, 7): | ||
| 2112 | + | ||
| 2113 | + if sys.version_info[:2] >= (3, 7): | ||
| 2113 | 2114 | # Vendored from cpython typing._SpecialFrom | |
| 2114 | 2115 | class _SpecialForm(typing._Final, _root=True): | |
| 2115 | 2116 | __slots__ = ('_name', '__doc__', '_getitem') | |
@@ -2153,6 +2154,10 @@ def __subclasscheck__(self, cls): | |||
| 2153 | 2154 | def __getitem__(self, parameters): | |
| 2154 | 2155 | return self._getitem(self, parameters) | |
| 2155 | 2156 | ||
| 2157 | + | ||
| 2158 | + if hasattr(typing, "Self"): | ||
| 2159 | + Self = typing.Self | ||
| 2160 | + elif sys.version_info[:2] >= (3, 7): | ||
| 2156 | 2161 | @_SpecialForm | |
| 2157 | 2162 | def Self(self, params): | |
| 2158 | 2163 | """Used to spell the type of "self" in classes. | |
@@ -2195,6 +2200,69 @@ def __subclasscheck__(self, cls): | |||
| 2195 | 2200 | Self = _Self(_root=True) | |
| 2196 | 2201 | ||
| 2197 | 2202 | ||
| 2203 | + if hasattr(typing, "Never"): | ||
| 2204 | + Never = typing.Never | ||
| 2205 | + elif sys.version_info[:2] >= (3, 7): | ||
| 2206 | + @_SpecialForm | ||
| 2207 | + def Never(self, params): | ||
| 2208 | + """The bottom type, a type that has no members. | ||
| 2209 | + | ||
| 2210 | + This can be used to define a function that should never be | ||
| 2211 | + called, or a function that never returns:: | ||
| 2212 | + | ||
| 2213 | + from typing_extensions import Never | ||
| 2214 | + | ||
| 2215 | + def never_call_me(arg: Never) -> None: | ||
| 2216 | + pass | ||
| 2217 | + | ||
| 2218 | + def int_or_str(arg: int | str) -> None: | ||
| 2219 | + never_call_me(arg) # type checker error | ||
| 2220 | + match arg: | ||
| 2221 | + case int(): | ||
| 2222 | + print("It's an int") | ||
| 2223 | + case str(): | ||
| 2224 | + print("It's a str") | ||
| 2225 | + case _: | ||
| 2226 | + never_call_me(arg) # ok, arg is of type Never | ||
| 2227 | + | ||
| 2228 | + """ | ||
| 2229 | + | ||
| 2230 | + raise TypeError(f"{self} is not subscriptable") | ||
| 2231 | + else: | ||
| 2232 | + class _Never(typing._FinalTypingBase, _root=True): | ||
| 2233 | + """The bottom type, a type that has no members. | ||
| 2234 | + | ||
| 2235 | + This can be used to define a function that should never be | ||
| 2236 | + called, or a function that never returns:: | ||
| 2237 | + | ||
| 2238 | + from typing_extensions import Never | ||
| 2239 | + | ||
| 2240 | + def never_call_me(arg: Never) -> None: | ||
| 2241 | + pass | ||
| 2242 | + | ||
| 2243 | + def int_or_str(arg: int | str) -> None: | ||
| 2244 | + never_call_me(arg) # type checker error | ||
| 2245 | + match arg: | ||
| 2246 | + case int(): | ||
| 2247 | + print("It's an int") | ||
| 2248 | + case str(): | ||
| 2249 | + print("It's a str") | ||
| 2250 | + case _: | ||
| 2251 | + never_call_me(arg) # ok, arg is of type Never | ||
| 2252 | + | ||
| 2253 | + """ | ||
| 2254 | + | ||
| 2255 | + __slots__ = () | ||
| 2256 | + | ||
| 2257 | + def __instancecheck__(self, obj): | ||
| 2258 | + raise TypeError(f"{self} cannot be used with isinstance().") | ||
| 2259 | + | ||
| 2260 | + def __subclasscheck__(self, cls): | ||
| 2261 | + raise TypeError(f"{self} cannot be used with issubclass().") | ||
| 2262 | + | ||
| 2263 | + Never = _Never(_root=True) | ||
| 2264 | + | ||
| 2265 | + | ||
| 2198 | 2266 | if hasattr(typing, 'Required'): | |
| 2199 | 2267 | Required = typing.Required | |
| 2200 | 2268 | NotRequired = typing.NotRequired | |
@@ -2377,6 +2445,32 @@ def reveal_type(__obj: T) -> T: | |||
| 2377 | 2445 | return __obj | |
| 2378 | 2446 | ||
| 2379 | 2447 | ||
| 2448 | + if hasattr(typing, "assert_never"): | ||
| 2449 | + assert_never = typing.assert_never | ||
| 2450 | + else: | ||
| 2451 | + def assert_never(__arg: Never) -> Never: | ||
| 2452 | + """Assert to the type checker that a line of code is unreachable. | ||
| 2453 | + | ||
| 2454 | + Example:: | ||
| 2455 | + | ||
| 2456 | + def int_or_str(arg: int | str) -> None: | ||
| 2457 | + match arg: | ||
| 2458 | + case int(): | ||
| 2459 | + print("It's an int") | ||
| 2460 | + case str(): | ||
| 2461 | + print("It's a str") | ||
| 2462 | + case _: | ||
| 2463 | + assert_never(arg) | ||
| 2464 | + | ||
| 2465 | + If a type checker finds that a call to assert_never() is | ||
| 2466 | + reachable, it will emit an error. | ||
| 2467 | + | ||
| 2468 | + At runtime, this throws an exception when called. | ||
| 2469 | + | ||
| 2470 | + """ | ||
| 2471 | + raise AssertionError("Expected code to be unreachable") | ||
| 2472 | + | ||
| 2473 | + | ||
| 2380 | 2474 | if hasattr(typing, 'dataclass_transform'): | |
| 2381 | 2475 | dataclass_transform = typing.dataclass_transform | |
| 2382 | 2476 | else: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments