From a5fa921fe5141492532322a43461b86231c6f8d5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 30 Apr 2017 22:09:08 +0200 Subject: [PATCH 1/3] Routine updates to typing --- Lib/test/test_typing.py | 51 +++++++++++++++++++++++++++++++++++++---- Lib/typing.py | 35 ++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 20fc2219f7ba69..33d553ed6e3a4e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -6,7 +6,7 @@ from unittest import TestCase, main, skipUnless, SkipTest from copy import copy, deepcopy -from typing import Any +from typing import Any, NoReturn from typing import TypeVar, AnyStr from typing import T, KT, VT # Not in __all__. from typing import Union, Optional @@ -102,10 +102,6 @@ def test_cannot_instantiate(self): with self.assertRaises(TypeError): type(Any)() - def test_cannot_subscript(self): - with self.assertRaises(TypeError): - Any[int] - def test_any_works_with_alias(self): # These expressions must simply not fail. typing.Match[Any] @@ -113,6 +109,40 @@ def test_any_works_with_alias(self): typing.IO[Any] +class NoReturnTests(BaseTestCase): + + def test_noreturn_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, NoReturn) + + def test_noreturn_subclass_type_error(self): + with self.assertRaises(TypeError): + issubclass(Employee, NoReturn) + with self.assertRaises(TypeError): + issubclass(NoReturn, Employee) + + def test_repr(self): + self.assertEqual(repr(NoReturn), 'typing.NoReturn') + + def test_not_generic(self): + with self.assertRaises(TypeError): + NoReturn[int] + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class A(NoReturn): + pass + with self.assertRaises(TypeError): + class A(type(NoReturn)): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + NoReturn() + with self.assertRaises(TypeError): + type(NoReturn)() + + class TypeVarTests(BaseTestCase): def test_basic_plain(self): @@ -2273,6 +2303,14 @@ def _fields(self): return 'no chance for this' """) + with self.assertRaises(AttributeError): + exec(""" +class XMethBad2(NamedTuple): + x: int + def _source(self): + return 'no chance for this as well' +""") + @skipUnless(PY36, 'Python 3.6 required') def test_namedtuple_keyword_usage(self): LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int) @@ -2420,6 +2458,9 @@ def test_all(self): self.assertNotIn('sys', a) # Check that Text is defined. self.assertIn('Text', a) + # Check previously missing classes. + self.assertIn('SupportsBytes', a) + self.assertIn('SupportsComplex', a) if __name__ == '__main__': diff --git a/Lib/typing.py b/Lib/typing.py index 9a0f49099a3114..645bc6f8ae0edd 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -11,9 +11,9 @@ except ImportError: import collections as collections_abc # Fallback for PY3.2. try: - from types import SlotWrapperType, MethodWrapperType, MethodDescriptorType + from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType except ImportError: - SlotWrapperType = type(object.__init__) + WrapperDescriptorType = type(object.__init__) MethodWrapperType = type(object().__str__) MethodDescriptorType = type(str.join) @@ -63,6 +63,8 @@ # Structural checks, a.k.a. protocols. 'Reversible', 'SupportsAbs', + 'SupportsBytes', + 'SupportsComplex', 'SupportsFloat', 'SupportsInt', 'SupportsRound', @@ -420,6 +422,31 @@ def __subclasscheck__(self, cls): Any = _Any(_root=True) +class _NoReturn(_FinalTypingBase, _root=True): + """Special type indicating functions that never return. + Example:: + + from typing import NoReturn + + def stop() -> NoReturn: + raise Exception('no way') + + This type is invalid in other positions, e.g., ``List[NoReturn]`` + will fail in static type checkers. + """ + + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("NoReturn cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("NoReturn cannot be used with issubclass().") + + +NoReturn = _NoReturn(_root=True) + + class TypeVar(_TypingBase, _root=True): """Type variable. @@ -1450,7 +1477,7 @@ def _get_defaults(func): _allowed_types = (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.ModuleType, - SlotWrapperType, MethodWrapperType, MethodDescriptorType) + WrapperDescriptorType, MethodWrapperType, MethodDescriptorType) def get_type_hints(obj, globalns=None, localns=None): @@ -2051,7 +2078,7 @@ def _make_nmtuple(name, types): # attributes prohibited to set in NamedTuple class syntax _prohibited = ('__new__', '__init__', '__slots__', '__getnewargs__', '_fields', '_field_defaults', '_field_types', - '_make', '_replace', '_asdict') + '_make', '_replace', '_asdict', '_source') _special = ('__module__', '__name__', '__qualname__', '__annotations__') From 88fa2dcb75df84b08653ba99638fcf2efc4dfce5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 2 May 2017 08:40:54 +0200 Subject: [PATCH 2/3] Add corresponding misc/NEWS item --- Misc/NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Misc/NEWS b/Misc/NEWS index 026beff1954e5e..4ff4f4c6582e1b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -317,6 +317,10 @@ Extension Modules Library ------- +- bpo-28556: Various updates to typing module: add typing.NoReturn type, use + WrapperDescriptorType, minor bug-fixes. Original PRs by + Jim Fasarakis-Hilliard and Ivan Levkivskyi. + - bpo-30101: Add support for curses.A_ITALIC. - bpo-29822: inspect.isabstract() now works during __init_subclass__. Patch From a25dc3162ba19d7c487ff02d3a88fc9b8fd2847d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 2 May 2017 08:46:06 +0200 Subject: [PATCH 3/3] Remove occasionally included files --- tst.py | 8 -------- tst0.py | 13 ------------- 2 files changed, 21 deletions(-) delete mode 100644 tst.py delete mode 100644 tst0.py diff --git a/tst.py b/tst.py deleted file mode 100644 index fac1d2ebadd589..00000000000000 --- a/tst.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import NamedTuple - -class Employee(NamedTuple): - """Represents an employee.""" - name: str - id: int = 3 - def __repr__(self) -> str: - return f'' diff --git a/tst0.py b/tst0.py deleted file mode 100644 index 57f7c68fc82f5b..00000000000000 --- a/tst0.py +++ /dev/null @@ -1,13 +0,0 @@ -import unittest -import sys - -if __name__ == '__main__': - tc = unittest.TestCase('__init__') - sys.setrecursionlimit(50) - - step = 100 - list1 = list(range(1, step)) - list2 = list(range(step, step+step)) - - tc.assertEquals(list1, list2) -