[Python-checkins] cpython (merge 3.6 -> default): Issue #28556: Allow defining methods in NamedTuple class syntax (#362)

guido.van.rossum python-checkins at python.org
Sun Jan 22 20:49:06 EST 2017


https://hg.python.org/cpython/rev/69c5b800df86
changeset:   106272:69c5b800df86
parent:      106270:a7e69479ee22
parent:      106271:ba272f947c40
user:        Guido van Rossum <guido at python.org>
date:        Sun Jan 22 17:47:22 2017 -0800
summary:
  Issue #28556: Allow defining methods in NamedTuple class syntax (#362) (3.6->3.7)

files:
  Lib/test/test_typing.py |  32 ++++++++++++++++++++++++----
  Lib/typing.py           |   4 +++
  2 files changed, 31 insertions(+), 5 deletions(-)


diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -612,8 +612,10 @@
         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 @@
                 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 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 @@
     # 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 @@
     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)
 
@@ -2039,6 +2054,13 @@
 """)
 
     @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)
         nick = LocalEmployee('Nick', 25)
diff --git a/Lib/typing.py b/Lib/typing.py
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2000,6 +2000,10 @@
                                         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
 
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list