Skip to content

Commit 11e6c72

Browse files
committed
[mypyc] Add basic support for class-based named tuples
Not all possible variants are tested, but at least simple things work. Fixes mypyc/mypyc#719.
1 parent 5496bf9 commit 11e6c72

File tree

3 files changed

+23
-1
lines changed

3 files changed

+23
-1
lines changed

mypyc/irbuild/classdef.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,16 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value:
213213
214214
The tuple is passed to the metaclass constructor.
215215
"""
216+
is_named_tuple = cdef.info.is_named_tuple
216217
ir = builder.mapper.type_to_ir[cdef.info]
217218
bases = []
218219
for cls in cdef.info.mro[1:]:
219220
if cls.fullname == 'builtins.object':
220221
continue
222+
if is_named_tuple and cls.fullname in ('typing.Sequence', 'typing.Iterable'):
223+
# HAX: Synthesized base classes added by mypy don't exist at runtime, so skip them.
224+
# This could break if they were added explicitly, though...
225+
continue
221226
# Add the current class to the base classes list of concrete subclasses
222227
if cls in builder.mapper.type_to_ir:
223228
base_ir = builder.mapper.type_to_ir[cls]
@@ -237,6 +242,8 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value:
237242
# In Python 3.9 TypedDict is not a real type.
238243
name = '_TypedDict'
239244
base = builder.get_module_attr(module, name, cdef.line)
245+
elif is_named_tuple and cls.fullname == 'builtins.tuple':
246+
base = builder.get_module_attr('typing', 'NamedTuple', cdef.line)
240247
else:
241248
base = builder.load_global_str(cls.name, cdef.line)
242249
bases.append(base)
@@ -455,7 +462,7 @@ def cache_class_attrs(builder: IRBuilder,
455462
attrs_to_cache: List[Tuple[Lvalue, RType]],
456463
cdef: ClassDef) -> None:
457464
"""Add class attributes to be cached to the global cache."""
458-
typ = builder.load_native_type_object(cdef.fullname)
465+
typ = builder.load_native_type_object(cdef.info.fullname)
459466
for lval, rtype in attrs_to_cache:
460467
assert isinstance(lval, NameExpr)
461468
rval = builder.py_get_attr(typ, lval.name, cdef.line)

mypyc/irbuild/util.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def is_extension_class(cdef: ClassDef) -> bool:
8484
return False
8585
if cdef.info.typeddict_type:
8686
return False
87+
if cdef.info.is_named_tuple:
88+
return False
8789
if (cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in (
8890
'abc.ABCMeta', 'typing.TypingMeta', 'typing.GenericMeta')):
8991
return False

mypyc/test-data/run-misc.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,19 @@ def test_non_total_typed_dict() -> None:
775775
assert d3['c'] == 3
776776
assert d4['d'] == 4
777777

778+
[case testClassBasedNamedTuple]
779+
from typing import NamedTuple
780+
781+
class NT(NamedTuple):
782+
a: int
783+
784+
def test_named_tuple() -> None:
785+
t = NT(a=1)
786+
assert t.a == 1
787+
assert type(t) is NT
788+
assert isinstance(t, tuple)
789+
assert not isinstance(tuple([1]), NT)
790+
778791
[case testUnion]
779792
from typing import Union
780793

0 commit comments

Comments
 (0)