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