]> granicus.if.org Git - python/commitdiff
Issue #28556: allow default values in class form of NamedTuple -- Jelle Zijlstra
authorGuido van Rossum <guido@python.org>
Wed, 18 Jan 2017 16:03:50 +0000 (08:03 -0800)
committerGuido van Rossum <guido@python.org>
Wed, 18 Jan 2017 16:03:50 +0000 (08:03 -0800)
Lib/test/test_typing.py
Lib/typing.py

index 73bcda1e01a0be908ac2b1720c39b9637a8daa56..44712b699bb48d7586aa5fede37220f54d1bdd4b 100644 (file)
@@ -1400,6 +1400,10 @@ class G(Generic[T]):
 class CoolEmployee(NamedTuple):
     name: str
     cool: int
+
+class CoolEmployeeWithDefault(NamedTuple):
+    name: str
+    cool: int = 0
 """
 
 if PY36:
@@ -1959,6 +1963,28 @@ class NamedTupleTests(BaseTestCase):
                          collections.OrderedDict(name=str, cool=int))
         self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__)
 
+    @skipUnless(PY36, 'Python 3.6 required')
+    def test_annotation_usage_with_default(self):
+        jelle = CoolEmployeeWithDefault('Jelle')
+        self.assertIsInstance(jelle, CoolEmployeeWithDefault)
+        self.assertIsInstance(jelle, tuple)
+        self.assertEqual(jelle.name, 'Jelle')
+        self.assertEqual(jelle.cool, 0)
+        cooler_employee = CoolEmployeeWithDefault('Sjoerd', 1)
+        self.assertEqual(cooler_employee.cool, 1)
+
+        self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault')
+        self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool'))
+        self.assertEqual(CoolEmployeeWithDefault._field_types, dict(name=str, cool=int))
+        self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0))
+
+        with self.assertRaises(TypeError):
+            exec("""
+class NonDefaultAfterDefault(NamedTuple):
+    x: int = 3
+    y: int
+""")
+
     @skipUnless(PY36, 'Python 3.6 required')
     def test_namedtuple_keyword_usage(self):
         LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
index b798830f6514f017072588fb1d4913f250c057c8..0aeb089d5d61a595832b7826f293aeb9198f55ce 100644 (file)
@@ -1959,7 +1959,22 @@ class NamedTupleMeta(type):
             raise TypeError("Class syntax for NamedTuple is only supported"
                             " in Python 3.6+")
         types = ns.get('__annotations__', {})
-        return _make_nmtuple(typename, types.items())
+        nm_tpl = _make_nmtuple(typename, types.items())
+        defaults = []
+        defaults_dict = {}
+        for field_name in types:
+            if field_name in ns:
+                default_value = ns[field_name]
+                defaults.append(default_value)
+                defaults_dict[field_name] = default_value
+            elif defaults:
+                raise TypeError("Non-default namedtuple field {field_name} cannot follow default"
+                                " field(s) {default_names}"
+                                .format(field_name=field_name,
+                                        default_names=', '.join(defaults_dict.keys())))
+        nm_tpl.__new__.__defaults__ = tuple(defaults)
+        nm_tpl._field_defaults = defaults_dict
+        return nm_tpl
 
 class NamedTuple(metaclass=NamedTupleMeta):
     """Typed version of namedtuple.