def analyze_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool ) -> Tuple[bool, Optional[TypeInfo]]: """Analyze if given class definition can be a named tuple definition. Return a tuple where first item indicates whether this can possibly be a named tuple, and the second item is the corresponding TypeInfo (may be None if not ready and should be deferred). """ for base_expr in defn.base_type_exprs: if isinstance(base_expr, RefExpr): self.api.accept(base_expr) if base_expr.fullname == 'typing.NamedTuple': result = self.check_namedtuple_classdef(defn, is_stub_file) if result is None: # This is a valid named tuple, but some types are incomplete. return True, None items, types, default_items = result info = self.build_namedtuple_typeinfo( defn.name, items, types, default_items, defn.line) defn.info = info defn.analyzed = NamedTupleExpr(info, is_typed=True) defn.analyzed.line = defn.line defn.analyzed.column = defn.column # All done: this is a valid named tuple with all types known. return True, info # This can't be a valid named tuple. return False, None
def check_namedtuple(self, node: Expression, var_name: Optional[str], is_func_scope: bool) -> Optional[TypeInfo]: """Check if a call defines a namedtuple. The optional var_name argument is the name of the variable to which this is assigned, if any. If it does, return the corresponding TypeInfo. Return None otherwise. If the definition is invalid but looks like a namedtuple, report errors but return (some) TypeInfo. """ if not isinstance(node, CallExpr): return None call = node callee = call.callee if not isinstance(callee, RefExpr): return None fullname = callee.fullname if fullname == 'collections.namedtuple': is_typed = False elif fullname == 'typing.NamedTuple': is_typed = True else: return None items, types, defaults, ok = self.parse_namedtuple_args(call, fullname) if not ok: # Error. Construct dummy return value. return self.build_namedtuple_typeinfo('namedtuple', [], [], {}) name = cast(StrExpr, call.args[0]).value if name != var_name or is_func_scope: # Give it a unique name derived from the line number. name += '@' + str(call.line) if len(defaults) > 0: default_items = { arg_name: default for arg_name, default in zip(items[-len(defaults):], defaults) } else: default_items = {} info = self.build_namedtuple_typeinfo(name, items, types, default_items) # Store it as a global just in case it would remain anonymous. # (Or in the nearest class if there is one.) stnode = SymbolTableNode(GDEF, info) self.api.add_symbol_table_node(name, stnode) call.analyzed = NamedTupleExpr(info, is_typed=is_typed) call.analyzed.set_line(call.line, call.column) return info
def analyze_namedtuple_classdef(self, defn: ClassDef) -> Optional[TypeInfo]: # special case for NamedTuple for base_expr in defn.base_type_exprs: if isinstance(base_expr, RefExpr): self.api.accept(base_expr) if base_expr.fullname == 'typing.NamedTuple': node = self.api.lookup(defn.name, defn) if node is not None: node.kind = GDEF # TODO in process_namedtuple_definition also applies here items, types, default_items = self.check_namedtuple_classdef(defn) info = self.build_namedtuple_typeinfo( defn.name, items, types, default_items) node.node = info defn.info.replaced = info defn.info = info defn.analyzed = NamedTupleExpr(info, is_typed=True) defn.analyzed.line = defn.line defn.analyzed.column = defn.column return info return None
def store_namedtuple_info(self, info: TypeInfo, name: str, call: CallExpr, is_typed: bool) -> None: self.api.add_symbol(name, info, call) call.analyzed = NamedTupleExpr(info, is_typed=is_typed) call.analyzed.set_line(call.line, call.column)
def visit_namedtuple_expr(self, node: NamedTupleExpr) -> Node: return NamedTupleExpr(node.info)
def visit_namedtuple_expr(self, node: NamedTupleExpr) -> None: super().visit_namedtuple_expr(node) node.info = self.fixup_and_reset_typeinfo(node.info) self.process_synthetic_type_info(node.info)
def store_namedtuple_info(self, info: TypeInfo, name: str, call: CallExpr, is_typed: bool) -> None: stnode = SymbolTableNode(GDEF, info) self.api.add_symbol_table_node(name, stnode) call.analyzed = NamedTupleExpr(info, is_typed=is_typed) call.analyzed.set_line(call.line, call.column)
def visit_namedtuple_expr(self, node: NamedTupleExpr) -> None: super().visit_namedtuple_expr(node) node.info = self.fixup(node.info) self.process_type_info(node.info)