From 965dd76e9060e27e2253ba8c8d21a142b178720d Mon Sep 17 00:00:00 2001 From: Yurii Karabas <1998uriyyo@gmail.com> Date: Tue, 20 Jul 2021 16:20:38 +0300 Subject: [PATCH] bpo-44353: Refactor typing.NewType into callable class (GH-27250) --- Lib/test/test_typing.py | 20 ++++++++++++++ Lib/typing.py | 27 +++++++++++++++---- .../2021-07-19-22-43-15.bpo-44353.HF81_Q.rst | 2 ++ 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-07-19-22-43-15.bpo-44353.HF81_Q.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b696447d098..169b92b6513 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3691,6 +3691,26 @@ def test_errors(self): class D(UserName): pass + def test_or(self): + UserId = NewType('UserId', int) + + self.assertEqual(UserId | int, Union[UserId, int]) + self.assertEqual(int | UserId, Union[int, UserId]) + + self.assertEqual(get_args(UserId | int), (UserId, int)) + self.assertEqual(get_args(int | UserId), (int, UserId)) + + def test_special_attrs(self): + UserId = NewType('UserId', int) + + self.assertEqual(UserId.__name__, 'UserId') + self.assertEqual(UserId.__qualname__, 'UserId') + self.assertEqual(UserId.__module__, __name__) + + def test_repr(self): + UserId = NewType('UserId', int) + + self.assertEqual(repr(UserId), f'{__name__}.UserId') class NamedTupleTests(BaseTestCase): class NestedEmployee(NamedTuple): diff --git a/Lib/typing.py b/Lib/typing.py index 4a6efee8020..1aff0a1b302 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1374,6 +1374,12 @@ def _no_init(self, *args, **kwargs): if type(self)._is_protocol: raise TypeError('Protocols cannot be instantiated') +def _callee(depth=2, default=None): + try: + return sys._getframe(depth).f_globals['__name__'] + except (AttributeError, ValueError): # For platforms without _getframe() + return default + def _allow_reckless_class_checks(depth=3): """Allow instance and class checks for special stdlib modules. @@ -2350,7 +2356,7 @@ class body be required. TypedDict.__mro_entries__ = lambda bases: (_TypedDict,) -def NewType(name, tp): +class NewType: """NewType creates simple unique types with almost zero runtime overhead. NewType(name, tp) is considered a subtype of tp by static type checkers. At runtime, NewType(name, tp) returns @@ -2369,12 +2375,23 @@ def name_by_id(user_id: UserId) -> str: num = UserId(5) + 1 # type: int """ - def new_type(x): + def __init__(self, name, tp): + self.__name__ = name + self.__qualname__ = name + self.__module__ = _callee(default='typing') + self.__supertype__ = tp + + def __repr__(self): + return f'{self.__module__}.{self.__qualname__}' + + def __call__(self, x): return x - new_type.__name__ = name - new_type.__supertype__ = tp - return new_type + def __or__(self, other): + return Union[self, other] + + def __ror__(self, other): + return Union[other, self] # Python-version-specific alias (Python 2: unicode; Python 3: str) diff --git a/Misc/NEWS.d/next/Library/2021-07-19-22-43-15.bpo-44353.HF81_Q.rst b/Misc/NEWS.d/next/Library/2021-07-19-22-43-15.bpo-44353.HF81_Q.rst new file mode 100644 index 00000000000..8a1e0f77b31 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-07-19-22-43-15.bpo-44353.HF81_Q.rst @@ -0,0 +1,2 @@ +Refactor ``typing.NewType`` from function into callable class. Patch +provided by Yurii Karabas.