Skip to content

Commit

Permalink
Issue python#28556: allow default values in class form of NamedTuple …
Browse files Browse the repository at this point in the history
…-- Jelle Zijlstra
  • Loading branch information
gvanrossum committed Jan 18, 2017
1 parent 37f183d commit 3c268be
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
26 changes: 26 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -1959,6 +1963,28 @@ def test_annotation_usage(self):
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)
Expand Down
17 changes: 16 additions & 1 deletion Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1959,7 +1959,22 @@ def __new__(cls, typename, bases, ns):
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.
Expand Down

0 comments on commit 3c268be

Please sign in to comment.