Skip to content

Commit 2158596

Browse files
committed
Add support for super() in NamedTuple subclasses
1 parent 7d27561 commit 2158596

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed

Doc/library/typing.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,9 @@ types.
23412341
def __repr__(self) -> str:
23422342
return f'<Employee {self.name}, id={self.id}>'
23432343

2344+
Calls to :func:`super` are supported inside user-defined methods of ``NamedTuple`` subclasses
2345+
to reuse functionality from built-in classes :class:`tuple` and :class:`object`.
2346+
23442347
``NamedTuple`` subclasses can be generic::
23452348

23462349
class Group[T](NamedTuple):

Lib/collections/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def __ror__(self, other):
358358
except ImportError:
359359
_tuplegetter = lambda index, doc: property(_itemgetter(index), doc=doc)
360360

361-
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None):
361+
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None, _classcell=None):
362362
"""Returns a new subclass of tuple with named fields.
363363
364364
>>> Point = namedtuple('Point', ['x', 'y'])
@@ -508,6 +508,10 @@ def __getnewargs__(self):
508508
'__getnewargs__': __getnewargs__,
509509
'__match_args__': field_names,
510510
}
511+
512+
if _classcell is not None:
513+
class_namespace["__classcell__"] = _classcell
514+
511515
for index, name in enumerate(field_names):
512516
doc = _sys.intern(f'Alias for field number {index}')
513517
class_namespace[name] = _tuplegetter(index, doc)

Lib/test/test_typing.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8135,6 +8135,25 @@ class Group(NamedTuple):
81358135
self.assertIs(type(a), Group)
81368136
self.assertEqual(a, (1, [2]))
81378137

8138+
def test_classcell_access(self):
8139+
# See #85795: __class__ not set defining 'X' as <class '__main__.X'>
8140+
class AspiringTriager(NamedTuple):
8141+
name: str = "Bartosz"
8142+
8143+
@property
8144+
def tablename(self):
8145+
return __class__.__name__.lower() + "s"
8146+
8147+
def count(self, item):
8148+
if item == "Bartosz":
8149+
return super().count(item)
8150+
return -1
8151+
8152+
aspiring_triager = AspiringTriager()
8153+
self.assertEqual(aspiring_triager.tablename, "aspiringtriagers")
8154+
self.assertEqual(aspiring_triager.count("Bartosz"), 1)
8155+
self.assertEqual(aspiring_triager.count("Peter"), -1) # already a triager!
8156+
81388157
def test_namedtuple_keyword_usage(self):
81398158
with self.assertWarnsRegex(
81408159
DeprecationWarning,

Lib/typing.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,9 +2929,9 @@ def __round__(self, ndigits: int = 0) -> T:
29292929
pass
29302930

29312931

2932-
def _make_nmtuple(name, fields, annotate_func, module, defaults = ()):
2933-
nm_tpl = collections.namedtuple(name, fields,
2934-
defaults=defaults, module=module)
2932+
def _make_nmtuple(name, fields, annotate_func, module, defaults = (), _classcell=None):
2933+
nm_tpl = collections.namedtuple(name, fields, defaults=defaults,
2934+
module=module, _classcell=_classcell)
29352935
nm_tpl.__annotate__ = nm_tpl.__new__.__annotate__ = annotate_func
29362936
return nm_tpl
29372937

@@ -3000,7 +3000,7 @@ def annotate(format):
30003000
f"{', '.join(default_names)}")
30013001
nm_tpl = _make_nmtuple(typename, field_names, annotate,
30023002
defaults=[ns[n] for n in default_names],
3003-
module=ns['__module__'])
3003+
module=ns['__module__'], _classcell=ns.pop("__classcell__", None))
30043004
nm_tpl.__bases__ = bases
30053005
if Generic in bases:
30063006
class_getitem = _generic_class_getitem

0 commit comments

Comments
 (0)