]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #28556: Allow defining methods in NamedTuple class syntax (#362)
authorGuido van Rossum <guido@python.org>
Mon, 23 Jan 2017 01:47:20 +0000 (17:47 -0800)
committerGuido van Rossum <guido@python.org>
Mon, 23 Jan 2017 01:47:20 +0000 (17:47 -0800)
Lib/test/test_typing.py
Lib/typing.py

index e0a9b3e1a313fb295f9723ffad8228ab858d82e9..fce6b5aaffdf932276477b95925bca1e29c5282c 100644 (file)
@@ -612,8 +612,10 @@ class GenericTests(BaseTestCase):
         self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]')
         self.assertEqual(repr(List[Tuple[T, TS]][int, T]),
                          'typing.List[typing.Tuple[int, ~T]]')
-        self.assertEqual(repr(List[Tuple[T, T]][List[int]]),
-                 'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]')
+        self.assertEqual(
+            repr(List[Tuple[T, T]][List[int]]),
+            'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]'
+        )
 
     def test_new_repr_bare(self):
         T = TypeVar('T')
@@ -684,8 +686,10 @@ class GenericTests(BaseTestCase):
                 raise NotImplementedError
             if tp.__args__:
                 KT, VT = tp.__args__
-                return all(isinstance(k, KT) and isinstance(v, VT)
-                   for k, v in obj.items())
+                return all(
+                    isinstance(k, KT) and isinstance(v, VT)
+                    for k, v in obj.items()
+                )
         self.assertTrue(naive_dict_check({'x': 1}, typing.Dict[str, int]))
         self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int]))
         with self.assertRaises(NotImplementedError):
@@ -1409,6 +1413,16 @@ class CoolEmployee(NamedTuple):
 class CoolEmployeeWithDefault(NamedTuple):
     name: str
     cool: int = 0
+
+class XMeth(NamedTuple):
+    x: int
+    def double(self):
+        return 2 * self.x
+
+class XMethBad(NamedTuple):
+    x: int
+    def _fields(self):
+        return 'no chance for this'
 """
 
 if PY36:
@@ -1417,6 +1431,7 @@ else:
     # fake names for the sake of static analysis
     ann_module = ann_module2 = ann_module3 = None
     A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object
+    XMeth = XMethBad = object
 
 gth = get_type_hints
 
@@ -1750,7 +1765,7 @@ class CollectionsAbcTests(BaseTestCase):
     def test_async_generator(self):
         ns = {}
         exec("async def f():\n"
-            "    yield 42\n", globals(), ns)
+             "    yield 42\n", globals(), ns)
         g = ns['f']()
         self.assertIsSubclass(type(g), typing.AsyncGenerator)
 
@@ -2038,6 +2053,13 @@ class NonDefaultAfterDefault(NamedTuple):
     y: int
 """)
 
+    @skipUnless(PY36, 'Python 3.6 required')
+    def test_annotation_usage_with_methods(self):
+        self.assertEquals(XMeth(1).double(), 2)
+        self.assertEquals(XMeth(42).x, XMeth(42)[0])
+        self.assertEquals(XMethBad(1)._fields, ('x',))
+        self.assertEquals(XMethBad(1).__annotations__, {'x': int})
+
     @skipUnless(PY36, 'Python 3.6 required')
     def test_namedtuple_keyword_usage(self):
         LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
index eb42c19a9c3f29ea7e6385666a39a1b4150f4c74..c9e341753769e0133daba86bf82ff136d8d3e173 100644 (file)
@@ -2000,6 +2000,10 @@ class NamedTupleMeta(type):
                                         default_names=', '.join(defaults_dict.keys())))
         nm_tpl.__new__.__defaults__ = tuple(defaults)
         nm_tpl._field_defaults = defaults_dict
+        # update from user namespace without overriding special namedtuple attributes
+        for key in ns:
+            if not hasattr(nm_tpl, key):
+                setattr(nm_tpl, key, ns[key])
         return nm_tpl