]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44353: Refactor typing.NewType into callable class (GH-27250)
authorYurii Karabas <1998uriyyo@gmail.com>
Tue, 20 Jul 2021 13:20:38 +0000 (16:20 +0300)
committerGitHub <noreply@github.com>
Tue, 20 Jul 2021 13:20:38 +0000 (15:20 +0200)
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2021-07-19-22-43-15.bpo-44353.HF81_Q.rst [new file with mode: 0644]

index b696447d0982b74c955710c2c0162b97f95ea0c2..169b92b6513792e7967c33eab6b59f1ce7b38468 100644 (file)
@@ -3691,6 +3691,26 @@ class NewTypeTests(BaseTestCase):
             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):
index 4a6efee802042bff3a8c8d496b41a0f3017d0eeb..1aff0a1b3026d50927e8b90a0bef4fe89cb38cb4 100644 (file)
@@ -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 @@ _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
 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 NewType(name, tp):
         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 (file)
index 0000000..8a1e0f7
--- /dev/null
@@ -0,0 +1,2 @@
+Refactor ``typing.NewType`` from function into callable class. Patch
+provided by Yurii Karabas.