Skip to content

Commit baa4628

Browse files
elazarggvanrossum
authored andcommitted
selftype in namedtuple methods (#2408)
Fix one item from #2090.
1 parent e4fd655 commit baa4628

File tree

3 files changed

+25
-16
lines changed

3 files changed

+25
-16
lines changed

mypy/semanal.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
from mypy.errors import Errors, report_internal_error
7272
from mypy.types import (
7373
NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType,
74-
FunctionLike, UnboundType, TypeList, TypeVarDef,
74+
FunctionLike, UnboundType, TypeList, TypeVarDef, TypeType,
7575
TupleType, UnionType, StarType, EllipsisType, function_type)
7676
from mypy.nodes import implicit_module_attrs
7777
from mypy.typeanal import TypeAnalyser, TypeAnalyserPass3, analyze_type_alias
@@ -1803,31 +1803,41 @@ def add_field(var: Var, is_initialized_in_class: bool = False,
18031803
add_field(Var('_field_types', dictype), is_initialized_in_class=True)
18041804
add_field(Var('_source', strtype), is_initialized_in_class=True)
18051805

1806-
# TODO: SelfType should be bind to actual 'self'
1807-
this_type = fill_typevars(info)
1806+
tvd = TypeVarDef('NT', 1, [], fill_typevars(info))
1807+
selftype = TypeVarType(tvd)
18081808

18091809
def add_method(funcname: str, ret: Type, args: List[Argument], name=None,
18101810
is_classmethod=False) -> None:
1811-
if not is_classmethod:
1812-
args = [Argument(Var('self'), this_type, None, ARG_POS)] + args
1811+
if is_classmethod:
1812+
first = [Argument(Var('cls'), TypeType(selftype), None, ARG_POS)]
1813+
else:
1814+
first = [Argument(Var('self'), selftype, None, ARG_POS)]
1815+
args = first + args
1816+
18131817
types = [arg.type_annotation for arg in args]
18141818
items = [arg.variable.name() for arg in args]
18151819
arg_kinds = [arg.kind for arg in args]
18161820
signature = CallableType(types, arg_kinds, items, ret, function_type,
18171821
name=name or info.name() + '.' + funcname)
1818-
signature.is_classmethod_class = is_classmethod
1822+
signature.variables = [tvd]
18191823
func = FuncDef(funcname, args, Block([]), typ=signature)
18201824
func.info = info
18211825
func.is_class = is_classmethod
1822-
info.names[funcname] = SymbolTableNode(MDEF, func)
1826+
if is_classmethod:
1827+
v = Var(funcname, signature)
1828+
v.is_classmethod = True
1829+
v.info = info
1830+
dec = Decorator(func, [NameExpr('classmethod')], v)
1831+
info.names[funcname] = SymbolTableNode(MDEF, dec)
1832+
else:
1833+
info.names[funcname] = SymbolTableNode(MDEF, func)
18231834

1824-
add_method('_replace', ret=this_type,
1835+
add_method('_replace', ret=selftype,
18251836
args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED) for var in vars])
18261837
add_method('__init__', ret=NoneTyp(), name=info.name(),
18271838
args=[Argument(var, var.type, None, ARG_POS) for var in vars])
18281839
add_method('_asdict', args=[], ret=ordereddictype)
1829-
# FIX: make it actual class method
1830-
add_method('_make', ret=this_type, is_classmethod=True,
1840+
add_method('_make', ret=selftype, is_classmethod=True,
18311841
args=[Argument(Var('iterable', iterable_type), iterable_type, None, ARG_POS),
18321842
Argument(Var('new'), AnyType(), EllipsisExpr(), ARG_NAMED),
18331843
Argument(Var('len'), AnyType(), EllipsisExpr(), ARG_NAMED)])

test-data/unit/check-class-namedtuple.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ class X(NamedTuple):
283283
y: str
284284

285285
x: X
286-
reveal_type(x._replace()) # E: Revealed type is 'Tuple[builtins.int, builtins.str, fallback=__main__.X]'
286+
reveal_type(x._replace()) # E: Revealed type is '__main__.X*'
287287
x._replace(x=5)
288288
x._replace(y=5) # E: Argument 1 to X._replace has incompatible type "int"; expected "str"
289289

test-data/unit/check-namedtuple.test

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ from collections import namedtuple
258258

259259
X = namedtuple('X', ['x', 'y'])
260260
x = None # type: X
261-
reveal_type(x._replace()) # E: Revealed type is 'Tuple[Any, Any, fallback=__main__.X]'
261+
reveal_type(x._replace()) # E: Revealed type is '__main__.X*'
262262
x._replace(y=5)
263263
x._replace(x=3)
264264
x._replace(x=3, y=5)
@@ -279,19 +279,18 @@ from typing import NamedTuple
279279

280280
X = NamedTuple('X', [('x', int), ('y', str)])
281281
x = None # type: X
282-
reveal_type(x._replace()) # E: Revealed type is 'Tuple[builtins.int, builtins.str, fallback=__main__.X]'
282+
reveal_type(x._replace()) # E: Revealed type is '__main__.X*'
283283
x._replace(x=5)
284284
x._replace(y=5) # E: Argument 1 to X._replace has incompatible type "int"; expected "str"
285285

286-
287286
[case testNamedTupleMake]
288287
from typing import NamedTuple
289288

290289
X = NamedTuple('X', [('x', int), ('y', str)])
291-
reveal_type(X._make([5, 'a'])) # E: Revealed type is 'Tuple[builtins.int, builtins.str, fallback=__main__.X]'
290+
reveal_type(X._make([5, 'a'])) # E: Revealed type is '__main__.X*'
292291
X._make('a b') # E: Argument 1 to X._make has incompatible type "str"; expected Iterable[Any]
293292

294-
-- # FIX: not a proper class method
293+
-- # FIX: not a proper class method
295294
-- x = None # type: X
296295
-- reveal_type(x._make([5, 'a'])) # E: Revealed type is 'Tuple[builtins.int, builtins.str, fallback=__main__.X]'
297296
-- x._make('a b') # E: Argument 1 to X._make has incompatible type "str"; expected Iterable[Any]

0 commit comments

Comments
 (0)