Issue #28556: Allow defining methods in NamedTuple class syntax (#362) (3.5->3.6)

This commit is contained in:
Guido van Rossum 2017-01-22 17:47:21 -08:00
commit 744cd63df9
2 changed files with 31 additions and 5 deletions

View File

@ -612,8 +612,10 @@ class GenericTests(BaseTestCase):
self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]') self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]')
self.assertEqual(repr(List[Tuple[T, TS]][int, T]), self.assertEqual(repr(List[Tuple[T, TS]][int, T]),
'typing.List[typing.Tuple[int, ~T]]') 'typing.List[typing.Tuple[int, ~T]]')
self.assertEqual(repr(List[Tuple[T, T]][List[int]]), self.assertEqual(
'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]') repr(List[Tuple[T, T]][List[int]]),
'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]'
)
def test_new_repr_bare(self): def test_new_repr_bare(self):
T = TypeVar('T') T = TypeVar('T')
@ -684,8 +686,10 @@ class GenericTests(BaseTestCase):
raise NotImplementedError raise NotImplementedError
if tp.__args__: if tp.__args__:
KT, VT = tp.__args__ KT, VT = tp.__args__
return all(isinstance(k, KT) and isinstance(v, VT) return all(
for k, v in obj.items()) 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.assertTrue(naive_dict_check({'x': 1}, typing.Dict[str, int]))
self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int])) self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int]))
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
@ -1409,6 +1413,16 @@ class CoolEmployee(NamedTuple):
class CoolEmployeeWithDefault(NamedTuple): class CoolEmployeeWithDefault(NamedTuple):
name: str name: str
cool: int = 0 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: if PY36:
@ -1417,6 +1431,7 @@ else:
# fake names for the sake of static analysis # fake names for the sake of static analysis
ann_module = ann_module2 = ann_module3 = None ann_module = ann_module2 = ann_module3 = None
A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object
XMeth = XMethBad = object
gth = get_type_hints gth = get_type_hints
@ -2038,6 +2053,13 @@ class NonDefaultAfterDefault(NamedTuple):
y: int 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') @skipUnless(PY36, 'Python 3.6 required')
def test_namedtuple_keyword_usage(self): def test_namedtuple_keyword_usage(self):
LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int) LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)

View File

@ -2000,6 +2000,10 @@ class NamedTupleMeta(type):
default_names=', '.join(defaults_dict.keys()))) default_names=', '.join(defaults_dict.keys())))
nm_tpl.__new__.__defaults__ = tuple(defaults) nm_tpl.__new__.__defaults__ = tuple(defaults)
nm_tpl._field_defaults = defaults_dict 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 return nm_tpl