├── .pre-commit-config.yaml ├── README.md ├── .gitignore ├── tox.ini ├── pyproject.toml ├── LICENSE ├── .github └── workflows │ └── test.yml ├── mypy_extensions.py └── tests └── testextensions.py /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/PyCQA/flake8 3 | rev: 7.1.1 4 | hooks: 5 | - id: flake8 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mypy Extensions 2 | =============== 3 | 4 | The `mypy_extensions` module defines extensions to the Python standard 5 | library `typing` module that are supported by the mypy type checker and 6 | the mypyc compiler. 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | __pycache__ 3 | *.py[cod] 4 | *~ 5 | @* 6 | /build 7 | /env 8 | docs/build/ 9 | *.iml 10 | /out/ 11 | .venv/ 12 | .mypy_cache/ 13 | .incremental_checker_cache.json 14 | .cache 15 | dmypy.json 16 | .dmypy.json 17 | 18 | # Packages 19 | *.egg 20 | *.egg-info 21 | *.eggs 22 | 23 | # IDEs 24 | .idea 25 | *.swp 26 | .vscode 27 | 28 | # Operating Systems 29 | .DS_Store 30 | 31 | # Coverage Files 32 | htmlcov 33 | .coverage* 34 | 35 | # pytest cache 36 | .pytest_cache/ 37 | 38 | .tox 39 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 4.4.4 3 | skip_missing_interpreters = true 4 | envlist = py38, py39, py310, py311, py312, py313 5 | 6 | [testenv] 7 | description = run the test driver with {basepython} 8 | commands = python -We -m unittest discover tests 9 | 10 | [testenv:lint] 11 | description = check the code style 12 | basepython = python3 13 | deps = flake8 14 | commands = flake8 -j0 {posargs} 15 | 16 | [flake8] 17 | max-line-length = 99 18 | ignore = 19 | # multiple statements on one line (colon) (we use this for classes with empty body) 20 | E701 21 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core>=3.11,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "mypy_extensions" 7 | version = "1.2.0-dev" 8 | description = "Type system extensions for programs checked with the mypy type checker." 9 | readme = "README.md" 10 | authors = [ 11 | {name = "The mypy developers", email = "jukka.lehtosalo@iki.fi"} 12 | ] 13 | license = "MIT" 14 | classifiers = [ 15 | "Development Status :: 5 - Production/Stable", 16 | "Environment :: Console", 17 | "Intended Audience :: Developers", 18 | "Programming Language :: Python :: 3", 19 | "Programming Language :: Python :: 3.8", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | "Programming Language :: Python :: 3.13", 25 | "Topic :: Software Development", 26 | ] 27 | requires-python = ">=3.8" 28 | 29 | [project.urls] 30 | Homepage = "https://github.com/python/mypy_extensions" 31 | 32 | [tool.flit.sdist] 33 | include = ["tox.ini", "*/*test*.py"] 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mypy extensions are licensed under the terms of the MIT license, reproduced below. 2 | 3 | = = = = = 4 | 5 | The MIT License 6 | 7 | Copyright (c) 2016-2017 Jukka Lehtosalo and contributors 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a 10 | copy of this software and associated documentation files (the "Software"), 11 | to deal in the Software without restriction, including without limitation 12 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | and/or sell copies of the Software, and to permit persons to whom the 14 | Software is furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | 27 | = = = = = 28 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - "test-me-*" 8 | tags: 9 | - "*" 10 | pull_request: 11 | branches: 12 | - master 13 | 14 | # Set permissions at the job level. 15 | permissions: {} 16 | 17 | jobs: 18 | lint: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 10 21 | permissions: 22 | contents: read 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: actions/setup-python@v5 28 | with: 29 | python-version: 3.11 30 | 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install tox 35 | 36 | - name: Lint 37 | run: tox -e lint 38 | 39 | test: 40 | runs-on: ubuntu-latest 41 | timeout-minutes: 10 42 | permissions: 43 | contents: read 44 | 45 | strategy: 46 | fail-fast: false 47 | matrix: 48 | python: [ 49 | "3.8", 50 | "3.9", 51 | "3.10", 52 | "3.11", 53 | "3.12", 54 | "3.13", 55 | ] 56 | 57 | steps: 58 | - uses: actions/checkout@v4 59 | 60 | - uses: actions/setup-python@v5 61 | with: 62 | python-version: ${{ matrix.python }} 63 | allow-prereleases: true 64 | 65 | - name: Install dependencies 66 | run: | 67 | python -m pip install --upgrade pip 68 | pip install tox 69 | 70 | - name: Test 71 | run: tox -e py 72 | -------------------------------------------------------------------------------- /mypy_extensions.py: -------------------------------------------------------------------------------- 1 | """Defines experimental extensions to the standard "typing" module that are 2 | supported by the mypy typechecker. 3 | 4 | Example usage: 5 | from mypy_extensions import TypedDict 6 | """ 7 | 8 | from typing import Any, Dict 9 | 10 | import sys 11 | # _type_check is NOT a part of public typing API, it is used here only to mimic 12 | # the (convenient) behavior of types provided by typing module. 13 | from typing import _type_check # type: ignore 14 | 15 | 16 | def _check_fails(cls, other): 17 | try: 18 | if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', 'typing']: 19 | # Typed dicts are only for static structural subtyping. 20 | raise TypeError('TypedDict does not support instance and class checks') 21 | except (AttributeError, ValueError): 22 | pass 23 | return False 24 | 25 | 26 | def _dict_new(cls, *args, **kwargs): 27 | return dict(*args, **kwargs) 28 | 29 | 30 | def _typeddict_new(cls, _typename, _fields=None, **kwargs): 31 | total = kwargs.pop('total', True) 32 | if _fields is None: 33 | _fields = kwargs 34 | elif kwargs: 35 | raise TypeError("TypedDict takes either a dict or keyword arguments," 36 | " but not both") 37 | 38 | ns = {'__annotations__': dict(_fields), '__total__': total} 39 | try: 40 | # Setting correct module is necessary to make typed dict classes pickleable. 41 | ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') 42 | except (AttributeError, ValueError): 43 | pass 44 | 45 | return _TypedDictMeta(_typename, (), ns, _from_functional_call=True) 46 | 47 | 48 | class _TypedDictMeta(type): 49 | def __new__(cls, name, bases, ns, total=True, _from_functional_call=False): 50 | # Create new typed dict class object. 51 | # This method is called directly when TypedDict is subclassed, 52 | # or via _typeddict_new when TypedDict is instantiated. This way 53 | # TypedDict supports all three syntaxes described in its docstring. 54 | # Subclasses and instances of TypedDict return actual dictionaries 55 | # via _dict_new. 56 | 57 | # We need the `if TypedDict in globals()` check, 58 | # or we emit a DeprecationWarning when creating mypy_extensions.TypedDict itself 59 | if 'TypedDict' in globals(): 60 | import warnings 61 | warnings.warn( 62 | ( 63 | "mypy_extensions.TypedDict is deprecated, " 64 | "and will be removed in a future version. " 65 | "Use typing.TypedDict or typing_extensions.TypedDict instead." 66 | ), 67 | DeprecationWarning, 68 | stacklevel=(3 if _from_functional_call else 2) 69 | ) 70 | 71 | ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new 72 | tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) 73 | 74 | anns = ns.get('__annotations__', {}) 75 | msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" 76 | anns = {n: _type_check(tp, msg) for n, tp in anns.items()} 77 | for base in bases: 78 | anns.update(base.__dict__.get('__annotations__', {})) 79 | tp_dict.__annotations__ = anns 80 | if not hasattr(tp_dict, '__total__'): 81 | tp_dict.__total__ = total 82 | return tp_dict 83 | 84 | __instancecheck__ = __subclasscheck__ = _check_fails 85 | 86 | 87 | TypedDict = _TypedDictMeta('TypedDict', (dict,), {}) 88 | TypedDict.__module__ = __name__ 89 | TypedDict.__doc__ = \ 90 | """A simple typed name space. At runtime it is equivalent to a plain dict. 91 | 92 | TypedDict creates a dictionary type that expects all of its 93 | instances to have a certain set of keys, with each key 94 | associated with a value of a consistent type. This expectation 95 | is not checked at runtime but is only enforced by typecheckers. 96 | Usage:: 97 | 98 | Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) 99 | a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK 100 | b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check 101 | assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') 102 | 103 | The type info could be accessed via Point2D.__annotations__. TypedDict 104 | supports two additional equivalent forms:: 105 | 106 | Point2D = TypedDict('Point2D', x=int, y=int, label=str) 107 | 108 | class Point2D(TypedDict): 109 | x: int 110 | y: int 111 | label: str 112 | 113 | The latter syntax is only supported in Python 3.6+, while two other 114 | syntax forms work for 3.2+ 115 | """ 116 | 117 | # Argument constructors for making more-detailed Callables. These all just 118 | # return their type argument, to make them complete noops in terms of the 119 | # `typing` module. 120 | 121 | 122 | def Arg(type=Any, name=None): 123 | """A normal positional argument""" 124 | return type 125 | 126 | 127 | def DefaultArg(type=Any, name=None): 128 | """A positional argument with a default value""" 129 | return type 130 | 131 | 132 | def NamedArg(type=Any, name=None): 133 | """A keyword-only argument""" 134 | return type 135 | 136 | 137 | def DefaultNamedArg(type=Any, name=None): 138 | """A keyword-only argument with a default value""" 139 | return type 140 | 141 | 142 | def VarArg(type=Any): 143 | """A *args-style variadic positional argument""" 144 | return type 145 | 146 | 147 | def KwArg(type=Any): 148 | """A **kwargs-style variadic keyword argument""" 149 | return type 150 | 151 | 152 | # Return type that indicates a function does not return 153 | # Deprecated, use typing or typing_extensions variants instead 154 | class _DEPRECATED_NoReturn: pass 155 | 156 | 157 | def trait(cls): 158 | return cls 159 | 160 | 161 | def mypyc_attr(*attrs, **kwattrs): 162 | return lambda x: x 163 | 164 | 165 | # TODO: We may want to try to properly apply this to any type 166 | # variables left over... 167 | class _FlexibleAliasClsApplied: 168 | def __init__(self, val): 169 | self.val = val 170 | 171 | def __getitem__(self, args): 172 | return self.val 173 | 174 | 175 | class _FlexibleAliasCls: 176 | def __getitem__(self, args): 177 | return _FlexibleAliasClsApplied(args[-1]) 178 | 179 | 180 | FlexibleAlias = _FlexibleAliasCls() 181 | 182 | 183 | class _NativeIntMeta(type): 184 | def __instancecheck__(cls, inst): 185 | return isinstance(inst, int) 186 | 187 | 188 | _sentinel = object() 189 | 190 | 191 | class i64(metaclass=_NativeIntMeta): 192 | def __new__(cls, x=0, base=_sentinel): 193 | if base is not _sentinel: 194 | return int(x, base) 195 | return int(x) 196 | 197 | 198 | class i32(metaclass=_NativeIntMeta): 199 | def __new__(cls, x=0, base=_sentinel): 200 | if base is not _sentinel: 201 | return int(x, base) 202 | return int(x) 203 | 204 | 205 | class i16(metaclass=_NativeIntMeta): 206 | def __new__(cls, x=0, base=_sentinel): 207 | if base is not _sentinel: 208 | return int(x, base) 209 | return int(x) 210 | 211 | 212 | class u8(metaclass=_NativeIntMeta): 213 | def __new__(cls, x=0, base=_sentinel): 214 | if base is not _sentinel: 215 | return int(x, base) 216 | return int(x) 217 | 218 | 219 | for _int_type in i64, i32, i16, u8: 220 | _int_type.__doc__ = \ 221 | """A native fixed-width integer type when used with mypyc. 222 | 223 | In code not compiled with mypyc, behaves like the 'int' type in these 224 | runtime contexts: 225 | 226 | * {name}(x[, base=n]) converts a number or string to 'int' 227 | * isinstance(x, {name}) is the same as isinstance(x, int) 228 | """.format(name=_int_type.__name__) 229 | del _int_type 230 | 231 | 232 | def _warn_deprecation(name: str, module_globals: Dict[str, Any]) -> Any: 233 | if (val := module_globals.get(f"_DEPRECATED_{name}")) is None: 234 | msg = f"module '{__name__}' has no attribute '{name}'" 235 | raise AttributeError(msg) 236 | module_globals[name] = val 237 | if name in {"NoReturn"}: 238 | msg = ( 239 | f"'mypy_extensions.{name}' is deprecated, " 240 | "and will be removed in a future version. " 241 | f"Use 'typing.{name}' or 'typing_extensions.{name}' instead" 242 | ) 243 | else: 244 | assert False, f"Add deprecation message for 'mypy_extensions.{name}'" 245 | import warnings 246 | warnings.warn(msg, DeprecationWarning, stacklevel=3) 247 | return val 248 | 249 | 250 | def __getattr__(name: str) -> Any: 251 | return _warn_deprecation(name, module_globals=globals()) 252 | -------------------------------------------------------------------------------- /tests/testextensions.py: -------------------------------------------------------------------------------- 1 | import collections.abc 2 | import pickle 3 | import sys 4 | import typing 5 | import warnings 6 | from contextlib import contextmanager 7 | from unittest import TestCase, main 8 | from mypy_extensions import TypedDict, i64, i32, i16, u8 9 | 10 | 11 | class BaseTestCase(TestCase): 12 | 13 | def assertIsSubclass(self, cls, class_or_tuple, msg=None): 14 | if not issubclass(cls, class_or_tuple): 15 | message = '%r is not a subclass of %r' % (cls, class_or_tuple) 16 | if msg is not None: 17 | message += ' : %s' % msg 18 | raise self.failureException(message) 19 | 20 | def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): 21 | if issubclass(cls, class_or_tuple): 22 | message = '%r is a subclass of %r' % (cls, class_or_tuple) 23 | if msg is not None: 24 | message += ' : %s' % msg 25 | raise self.failureException(message) 26 | 27 | 28 | with warnings.catch_warnings(): 29 | warnings.simplefilter("ignore", category=DeprecationWarning) 30 | 31 | Label = TypedDict('Label', [('label', str)]) 32 | 33 | class Point2D(TypedDict): 34 | x: int 35 | y: int 36 | 37 | class LabelPoint2D(Point2D, Label): ... 38 | 39 | class Options(TypedDict, total=False): 40 | log_level: int 41 | log_path: str 42 | 43 | 44 | class TypedDictTests(BaseTestCase): 45 | @contextmanager 46 | def assert_typeddict_deprecated(self): 47 | with self.assertWarnsRegex( 48 | DeprecationWarning, "mypy_extensions.TypedDict is deprecated" 49 | ): 50 | yield 51 | 52 | def test_basics_iterable_syntax(self): 53 | with self.assert_typeddict_deprecated(): 54 | Emp = TypedDict('Emp', {'name': str, 'id': int}) 55 | self.assertIsSubclass(Emp, dict) 56 | self.assertIsSubclass(Emp, typing.MutableMapping) 57 | self.assertNotIsSubclass(Emp, collections.abc.Sequence) 58 | jim = Emp(name='Jim', id=1) 59 | self.assertIs(type(jim), dict) 60 | self.assertEqual(jim['name'], 'Jim') 61 | self.assertEqual(jim['id'], 1) 62 | self.assertEqual(Emp.__name__, 'Emp') 63 | self.assertEqual(Emp.__module__, __name__) 64 | self.assertEqual(Emp.__bases__, (dict,)) 65 | self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) 66 | self.assertEqual(Emp.__total__, True) 67 | 68 | def test_basics_keywords_syntax(self): 69 | with self.assert_typeddict_deprecated(): 70 | Emp = TypedDict('Emp', name=str, id=int) 71 | self.assertIsSubclass(Emp, dict) 72 | self.assertIsSubclass(Emp, typing.MutableMapping) 73 | self.assertNotIsSubclass(Emp, collections.abc.Sequence) 74 | jim = Emp(name='Jim', id=1) # type: ignore # mypy doesn't support keyword syntax yet 75 | self.assertIs(type(jim), dict) 76 | self.assertEqual(jim['name'], 'Jim') 77 | self.assertEqual(jim['id'], 1) 78 | self.assertEqual(Emp.__name__, 'Emp') 79 | self.assertEqual(Emp.__module__, __name__) 80 | self.assertEqual(Emp.__bases__, (dict,)) 81 | self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) 82 | self.assertEqual(Emp.__total__, True) 83 | 84 | def test_typeddict_errors(self): 85 | with self.assert_typeddict_deprecated(): 86 | Emp = TypedDict('Emp', {'name': str, 'id': int}) 87 | self.assertEqual(TypedDict.__module__, 'mypy_extensions') 88 | jim = Emp(name='Jim', id=1) 89 | with self.assertRaises(TypeError): 90 | isinstance({}, Emp) # type: ignore 91 | with self.assertRaises(TypeError): 92 | isinstance(jim, Emp) # type: ignore 93 | with self.assertRaises(TypeError): 94 | issubclass(dict, Emp) # type: ignore 95 | with self.assertRaises(TypeError), self.assert_typeddict_deprecated(): 96 | TypedDict('Hi', x=()) 97 | with self.assertRaises(TypeError), self.assert_typeddict_deprecated(): 98 | TypedDict('Hi', [('x', int), ('y', ())]) 99 | with self.assertRaises(TypeError): 100 | TypedDict('Hi', [('x', int)], y=int) 101 | 102 | def test_py36_class_syntax_usage(self): 103 | self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') # noqa 104 | self.assertEqual(LabelPoint2D.__module__, __name__) # noqa 105 | self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str}) # noqa 106 | self.assertEqual(LabelPoint2D.__bases__, (dict,)) # noqa 107 | self.assertEqual(LabelPoint2D.__total__, True) # noqa 108 | self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) # noqa 109 | not_origin = Point2D(x=0, y=1) # noqa 110 | self.assertEqual(not_origin['x'], 0) 111 | self.assertEqual(not_origin['y'], 1) 112 | other = LabelPoint2D(x=0, y=1, label='hi') # noqa 113 | self.assertEqual(other['label'], 'hi') 114 | 115 | def test_py36_class_usage_emits_deprecations(self): 116 | with self.assert_typeddict_deprecated(): 117 | class Foo(TypedDict): 118 | bar: int 119 | 120 | def test_pickle(self): 121 | global EmpD # pickle wants to reference the class by name 122 | with self.assert_typeddict_deprecated(): 123 | EmpD = TypedDict('EmpD', name=str, id=int) 124 | jane = EmpD({'name': 'jane', 'id': 37}) 125 | for proto in range(pickle.HIGHEST_PROTOCOL + 1): 126 | z = pickle.dumps(jane, proto) 127 | jane2 = pickle.loads(z) 128 | self.assertEqual(jane2, jane) 129 | self.assertEqual(jane2, {'name': 'jane', 'id': 37}) 130 | ZZ = pickle.dumps(EmpD, proto) 131 | EmpDnew = pickle.loads(ZZ) 132 | self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) 133 | 134 | def test_optional(self): 135 | with self.assert_typeddict_deprecated(): 136 | EmpD = TypedDict('EmpD', name=str, id=int) 137 | 138 | self.assertEqual(typing.Optional[EmpD], typing.Union[None, EmpD]) 139 | self.assertNotEqual(typing.List[EmpD], typing.Tuple[EmpD]) 140 | 141 | def test_total(self): 142 | with self.assert_typeddict_deprecated(): 143 | D = TypedDict('D', {'x': int}, total=False) 144 | self.assertEqual(D(), {}) 145 | self.assertEqual(D(x=1), {'x': 1}) 146 | self.assertEqual(D.__total__, False) 147 | 148 | self.assertEqual(Options(), {}) # noqa 149 | self.assertEqual(Options(log_level=2), {'log_level': 2}) # noqa 150 | self.assertEqual(Options.__total__, False) # noqa 151 | 152 | 153 | native_int_types = [i64, i32, i16, u8] 154 | 155 | 156 | class MypycNativeIntTests(TestCase): 157 | def test_construction(self): 158 | for native_int in native_int_types: 159 | self.assert_same(native_int(), 0) 160 | 161 | self.assert_same(native_int(0), 0) 162 | self.assert_same(native_int(1), 1) 163 | self.assert_same(native_int(-3), -3) 164 | self.assert_same(native_int(2**64), 2**64) 165 | self.assert_same(native_int(-2**64), -2**64) 166 | 167 | self.assert_same(native_int(1.234), 1) 168 | self.assert_same(native_int(2.634), 2) 169 | self.assert_same(native_int(-1.234), -1) 170 | self.assert_same(native_int(-2.634), -2) 171 | 172 | self.assert_same(native_int("0"), 0) 173 | self.assert_same(native_int("123"), 123) 174 | self.assert_same(native_int("abc", 16), 2748) 175 | self.assert_same(native_int("-101", base=2), -5) 176 | 177 | def test_isinstance(self): 178 | for native_int in native_int_types: 179 | assert isinstance(0, native_int) 180 | assert isinstance(1234, native_int) 181 | assert isinstance(True, native_int) 182 | assert not isinstance(1.0, native_int) 183 | 184 | def test_docstring(self): 185 | for native_int in native_int_types: 186 | # Just check that a docstring exists 187 | assert native_int.__doc__ 188 | 189 | def assert_same(self, x, y): 190 | assert type(x) is type(y) 191 | assert x == y 192 | 193 | 194 | class DeprecationTests(TestCase): 195 | def test_no_return_deprecation(self): 196 | del sys.modules["mypy_extensions"] 197 | with self.assertWarnsRegex( 198 | DeprecationWarning, "'mypy_extensions.NoReturn' is deprecated" 199 | ): 200 | import mypy_extensions 201 | mypy_extensions.NoReturn 202 | 203 | del sys.modules["mypy_extensions"] 204 | with self.assertWarnsRegex( 205 | DeprecationWarning, "'mypy_extensions.NoReturn' is deprecated" 206 | ): 207 | from mypy_extensions import NoReturn # noqa: F401 208 | 209 | 210 | if __name__ == '__main__': 211 | main() 212 | --------------------------------------------------------------------------------