57
57
FuncExpr , MDEF , FuncBase , Decorator , SetExpr , UndefinedExpr , TypeVarExpr ,
58
58
StrExpr , PrintStmt , ConditionalExpr , DucktypeExpr , DisjointclassExpr ,
59
59
ComparisonExpr , StarExpr , ARG_POS , ARG_NAMED , MroError , type_aliases ,
60
- YieldFromStmt , YieldFromExpr
60
+ YieldFromStmt , YieldFromExpr , NamedTupleExpr
61
61
)
62
62
from mypy .visitor import NodeVisitor
63
63
from mypy .traverser import TraverserVisitor
@@ -500,6 +500,11 @@ def analyze_base_classes(self, defn: ClassDef) -> None:
500
500
bases = List [Instance ]()
501
501
for i in range (len (defn .base_types )):
502
502
base = self .anal_type (defn .base_types [i ])
503
+ if isinstance (base , TupleType ):
504
+ if defn .info .tuple_type :
505
+ self .fail ("Class has two incompatible bases derived from tuple" , defn )
506
+ defn .info .tuple_type = base
507
+ base = base .fallback
503
508
if isinstance (base , Instance ):
504
509
defn .base_types [i ] = base
505
510
bases .append (base )
@@ -708,6 +713,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
708
713
self .store_declared_types (lvalue , s .type )
709
714
self .check_and_set_up_type_alias (s )
710
715
self .process_typevar_declaration (s )
716
+ self .process_namedtuple_definition (s )
711
717
712
718
def check_and_set_up_type_alias (self , s : AssignmentStmt ) -> None :
713
719
"""Check if assignment creates a type alias and set it up as needed."""
@@ -952,14 +958,156 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None:
952
958
return
953
959
else :
954
960
values = []
955
- # Yes, it's a valid type variable definition!
961
+ # Yes, it's a valid type variable definition! Add it to the symbol table.
956
962
node = self .lookup (name , s )
957
963
node .kind = UNBOUND_TVAR
958
964
typevar = TypeVarExpr (name , node .fullname , values )
959
965
typevar .line = call .line
960
966
call .analyzed = typevar
961
967
node .node = typevar
962
968
969
+ def process_namedtuple_definition (self , s : AssignmentStmt ) -> None :
970
+ """Check if s defines a namedtuple; if yes, store the definition in symbol table."""
971
+ if len (s .lvalues ) != 1 or not isinstance (s .lvalues [0 ], NameExpr ):
972
+ return
973
+ if not isinstance (s .rvalue , CallExpr ):
974
+ return
975
+ call = cast (CallExpr , s .rvalue )
976
+ named_tuple = self .check_namedtuple (call )
977
+ if named_tuple is None :
978
+ return
979
+ # Yes, it's a valid namedtuple definition. Add it to the symbol table.
980
+ lvalue = cast (NameExpr , s .lvalues [0 ])
981
+ name = lvalue .name
982
+ node = self .lookup (name , s )
983
+ node .kind = GDEF # TODO locally defined namedtuple
984
+ # TODO call.analyzed
985
+ node .node = named_tuple
986
+
987
+ def check_namedtuple (self , call : CallExpr ) -> TypeInfo :
988
+ """Check if a call defines a namedtuple.
989
+
990
+ If it does, return the corresponding TypeInfo. Return None otherwise.
991
+
992
+ If the definition is invalid but looks like a namedtuple,
993
+ report errors but return (some) TypeInfo.
994
+ """
995
+ if not isinstance (call .callee , RefExpr ):
996
+ return None
997
+ callee = cast (RefExpr , call .callee )
998
+ fullname = callee .fullname
999
+ if fullname not in ('collections.namedtuple' , 'typing.NamedTuple' ):
1000
+ return None
1001
+ items , types = self .parse_namedtuple_args (call , fullname )
1002
+ if not items :
1003
+ # Error. Construct dummy return value.
1004
+ error_classdef = ClassDef ('namedtuple' , Block ([]))
1005
+ info = TypeInfo (SymbolTable (), error_classdef )
1006
+ else :
1007
+ listexpr = cast (ListExpr , call .args [1 ])
1008
+ name = cast (StrExpr , call .args [0 ]).value
1009
+ info = self .build_namedtuple_typeinfo (name , items , types )
1010
+ call .analyzed = NamedTupleExpr (info ).set_line (call .line )
1011
+ return info
1012
+
1013
+ def parse_namedtuple_args (self , call : CallExpr ,
1014
+ fullname : str ) -> Tuple [List [str ], List [Type ]]:
1015
+ # TODO Share code with check_argument_count in checkexpr.py?
1016
+ args = call .args
1017
+ if len (args ) < 2 :
1018
+ return self .fail_namedtuple_arg ("Too few arguments for namedtuple()" , call )
1019
+ if len (args ) > 2 :
1020
+ return self .fail_namedtuple_arg ("Too many arguments for namedtuple()" , call )
1021
+ if call .arg_kinds != [ARG_POS , ARG_POS ]:
1022
+ return self .fail_namedtuple_arg ("Unexpected arguments to namedtuple()" , call )
1023
+ if not isinstance (args [0 ], StrExpr ):
1024
+ return self .fail_namedtuple_arg (
1025
+ "namedtuple() expects a string literal as the first argument" , call )
1026
+ if not isinstance (args [1 ], ListExpr ):
1027
+ return self .fail_namedtuple_arg (
1028
+ "List literal expected as the second argument to namedtuple()" , call )
1029
+ listexpr = cast (ListExpr , args [1 ])
1030
+ if fullname == 'collections.namedtuple' :
1031
+ # The fields argument contains just names, with implicit Any types.
1032
+ if any (not isinstance (item , StrExpr ) for item in listexpr .items ):
1033
+ return self .fail_namedtuple_arg ("String literal expected as namedtuple() item" ,
1034
+ call )
1035
+ items = [cast (StrExpr , item ).value for item in listexpr .items ]
1036
+ types = [AnyType () for _ in listexpr .items ] # type: List[Type]
1037
+ else :
1038
+ # The fields argument contains (name, type) tuples.
1039
+ items , types = self .parse_namedtuple_fields_with_types (listexpr .items , call )
1040
+ return items , types
1041
+
1042
+ def parse_namedtuple_fields_with_types (self , nodes : List [Node ],
1043
+ context : Context ) -> Tuple [List [str ], List [Type ]]:
1044
+ items = [] # type: List[str]
1045
+ types = [] # type: List[Type]
1046
+ for item in nodes :
1047
+ while isinstance (item , ParenExpr ):
1048
+ item = item .expr
1049
+ if isinstance (item , TupleExpr ):
1050
+ if len (item .items ) != 2 :
1051
+ return self .fail_namedtuple_arg ("Invalid NamedTuple field definition" ,
1052
+ item )
1053
+ name , type_node = item .items
1054
+ if isinstance (name , StrExpr ):
1055
+ items .append (name .value )
1056
+ else :
1057
+ return self .fail_namedtuple_arg ("Invalid NamedTuple() field name" , item )
1058
+ try :
1059
+ type = expr_to_unanalyzed_type (type_node )
1060
+ except TypeTranslationError :
1061
+ return self .fail_namedtuple_arg ('Invalid field type' , type_node )
1062
+ types .append (self .anal_type (type ))
1063
+ else :
1064
+ return self .fail_namedtuple_arg ("Tuple expected as NamedTuple() field" , item )
1065
+ return items , types
1066
+
1067
+ def fail_namedtuple_arg (self , message : str , context : Context ) -> Tuple [List [str ], List [Type ]]:
1068
+ self .fail (message , context )
1069
+ return [], []
1070
+
1071
+ def build_namedtuple_typeinfo (self , name : str , items : List [str ],
1072
+ types : List [Type ]) -> TypeInfo :
1073
+ symbols = SymbolTable ()
1074
+ class_def = ClassDef (name , Block ([]))
1075
+ class_def .fullname = self .qualified_name (name )
1076
+ info = TypeInfo (symbols , class_def )
1077
+ # Add named tuple items as attributes.
1078
+ # TODO: Make them read-only.
1079
+ for item , typ in zip (items , types ):
1080
+ var = Var (item )
1081
+ var .info = info
1082
+ var .type = typ
1083
+ symbols [item ] = SymbolTableNode (MDEF , var )
1084
+ # Add a __init__ method.
1085
+ init = self .make_namedtuple_init (info , items , types )
1086
+ symbols ['__init__' ] = SymbolTableNode (MDEF , init )
1087
+ info .tuple_type = TupleType (types , self .named_type ('__builtins__.tuple' ))
1088
+ info .mro = [info ] + info .tuple_type .fallback .type .mro
1089
+ return info
1090
+
1091
+ def make_namedtuple_init (self , info : TypeInfo , items : List [str ],
1092
+ types : List [Type ]) -> FuncDef :
1093
+ args = [Var (item ) for item in items ]
1094
+ for arg , type in zip (args , types ):
1095
+ arg .type = type
1096
+ # TODO: Make sure that the self argument name is not visible?
1097
+ args = [Var ('__self' )] + args
1098
+ arg_kinds = [ARG_POS ] * (len (items ) + 1 )
1099
+ signature = Callable ([cast (Type , None )] + types ,
1100
+ arg_kinds ,
1101
+ ['__self' ] + items ,
1102
+ NoneTyp (),
1103
+ self .named_type ('__builtins__.function' ),
1104
+ name = info .name ())
1105
+ return FuncDef ('__init__' ,
1106
+ args , arg_kinds ,
1107
+ [None ] * (len (items ) + 1 ),
1108
+ Block ([]),
1109
+ typ = signature )
1110
+
963
1111
def analyze_types (self , items : List [Node ]) -> List [Type ]:
964
1112
result = List [Type ]()
965
1113
for node in items :
@@ -1697,7 +1845,7 @@ def fail(self, msg: str, ctx: Context) -> None:
1697
1845
self .errors .report (ctx .get_line (), msg )
1698
1846
1699
1847
1700
- def self_type (typ : TypeInfo ) -> Instance :
1848
+ def self_type (typ : TypeInfo ) -> Union [ Instance , TupleType ] :
1701
1849
"""For a non-generic type, return instance type representing the type.
1702
1850
For a generic G type with parameters T1, .., Tn, return G[T1, ..., Tn].
1703
1851
"""
@@ -1706,7 +1854,11 @@ def self_type(typ: TypeInfo) -> Instance:
1706
1854
tv .append (TypeVar (typ .type_vars [i ], i + 1 ,
1707
1855
typ .defn .type_vars [i ].values ,
1708
1856
typ .defn .type_vars [i ].upper_bound ))
1709
- return Instance (typ , tv )
1857
+ inst = Instance (typ , tv )
1858
+ if typ .tuple_type is None :
1859
+ return inst
1860
+ else :
1861
+ return TupleType (typ .tuple_type .items , inst )
1710
1862
1711
1863
1712
1864
@overload
0 commit comments