|
53 | 53 | ImportFrom, ImportAll, Block, LDEF, NameExpr, MemberExpr,
|
54 | 54 | IndexExpr, TupleExpr, ListExpr, ExpressionStmt, ReturnStmt,
|
55 | 55 | RaiseStmt, AssertStmt, OperatorAssignmentStmt, WhileStmt,
|
56 |
| - ForStmt, BreakStmt, ContinueStmt, IfStmt, TryStmt, WithStmt, DelStmt, |
| 56 | + ForStmt, BreakStmt, ContinueStmt, IfStmt, TryStmt, WithStmt, DelStmt, PassStmt, |
57 | 57 | GlobalDecl, SuperExpr, DictExpr, CallExpr, RefExpr, OpExpr, UnaryExpr,
|
58 | 58 | SliceExpr, CastExpr, RevealTypeExpr, TypeApplication, Context, SymbolTable,
|
59 | 59 | SymbolTableNode, BOUND_TVAR, UNBOUND_TVAR, ListComprehension, GeneratorExpr,
|
|
63 | 63 | YieldFromExpr, NamedTupleExpr, TypedDictExpr, NonlocalDecl, SymbolNode,
|
64 | 64 | SetComprehension, DictionaryComprehension, TYPE_ALIAS, TypeAliasExpr,
|
65 | 65 | YieldExpr, ExecStmt, Argument, BackquoteExpr, ImportBase, AwaitExpr,
|
66 |
| - IntExpr, FloatExpr, UnicodeExpr, EllipsisExpr, |
| 66 | + IntExpr, FloatExpr, UnicodeExpr, EllipsisExpr, TempNode, |
67 | 67 | COVARIANT, CONTRAVARIANT, INVARIANT, UNBOUND_IMPORTED, LITERAL_YES,
|
68 | 68 | )
|
69 | 69 | from mypy.visitor import NodeVisitor
|
@@ -547,6 +547,8 @@ def check_function_signature(self, fdef: FuncItem) -> None:
|
547 | 547 |
|
548 | 548 | def visit_class_def(self, defn: ClassDef) -> None:
|
549 | 549 | self.clean_up_bases_and_infer_type_variables(defn)
|
| 550 | + if self.analyze_namedtuple_classdef(defn): |
| 551 | + return |
550 | 552 | self.setup_class_def_analysis(defn)
|
551 | 553 |
|
552 | 554 | self.bind_class_type_vars(defn)
|
@@ -713,6 +715,56 @@ def analyze_unbound_tvar(self, t: Type) -> Tuple[str, TypeVarExpr]:
|
713 | 715 | return unbound.name, cast(TypeVarExpr, sym.node)
|
714 | 716 | return None
|
715 | 717 |
|
| 718 | + def analyze_namedtuple_classdef(self, defn: ClassDef) -> bool: |
| 719 | + # special case for NamedTuple |
| 720 | + for base_expr in defn.base_type_exprs: |
| 721 | + if isinstance(base_expr, RefExpr): |
| 722 | + base_expr.accept(self) |
| 723 | + if base_expr.fullname == 'typing.NamedTuple': |
| 724 | + node = self.lookup(defn.name, defn) |
| 725 | + if node is not None: |
| 726 | + node.kind = GDEF # TODO in process_namedtuple_definition also applies here |
| 727 | + items, types = self.check_namedtuple_classdef(defn) |
| 728 | + node.node = self.build_namedtuple_typeinfo(defn.name, items, types) |
| 729 | + return True |
| 730 | + return False |
| 731 | + |
| 732 | + def check_namedtuple_classdef(self, defn: ClassDef) -> Tuple[List[str], List[Type]]: |
| 733 | + NAMEDTUP_CLASS_ERROR = ('Invalid statement in NamedTuple definition; ' |
| 734 | + 'expected "field_name: field_type"') |
| 735 | + if self.options.python_version < (3, 6): |
| 736 | + self.fail('NamedTuple class syntax is only supported in Python 3.6', defn) |
| 737 | + return [], [] |
| 738 | + if len(defn.base_type_exprs) > 1: |
| 739 | + self.fail('NamedTuple should be a single base', defn) |
| 740 | + items = [] # type: List[str] |
| 741 | + types = [] # type: List[Type] |
| 742 | + for stmt in defn.defs.body: |
| 743 | + if<
8000
/span> not isinstance(stmt, AssignmentStmt): |
| 744 | + # Still allow pass or ... (for empty namedtuples). |
| 745 | + if (not isinstance(stmt, PassStmt) and |
| 746 | + not (isinstance(stmt, ExpressionStmt) and |
| 747 | + isinstance(stmt.expr, EllipsisExpr))): |
| 748 | + self.fail(NAMEDTUP_CLASS_ERROR, stmt) |
| 749 | + elif len(stmt.lvalues) > 1 or not isinstance(stmt.lvalues[0], NameExpr): |
| 750 | + # An assignment, but an invalid one. |
| 751 | + self.fail(NAMEDTUP_CLASS_ERROR, stmt) |
| 752 | + else: |
| 753 | + # Append name and type in this case... |
| 754 | + name = stmt.lvalues[0].name |
| 755 | + items.append(name) |
| 756 | + types.append(AnyType() if stmt.type is None else self.anal_type(stmt.type)) |
| 757 | + # ...despite possible minor failures that allow further analyzis. |
| 758 | + if name.startswith('_'): |
| 759 | + self.fail('NamedTuple field name cannot start with an underscore: {}' |
| 760 | + .format(name), stmt) |
| 761 | + if stmt.type is None or hasattr(stmt, 'new_syntax') and not stmt.new_syntax: |
| 762 | + self.fail(NAMEDTUP_CLASS_ERROR, stmt) |
| 763 | + elif not isinstance(stmt.rvalue, TempNode): |
| 764 | + # x: int assigns rvalue to TempNode(AnyType()) |
| 765 | + self.fail('Right hand side values are not supported in NamedTuple', stmt) |
| 766 | + return items, types |
| 767 | + |
716 | 768 | def setup_class_def_analysis(self, defn: ClassDef) -> None:
|
717 | 769 | """Prepare for the analysis of a class definition."""
|
718 | 770 | if not defn.info:
|
|
0 commit comments