def _check_multi_statement_line(self, node, line): """Check for lines containing multiple statements.""" # Do not warn about multiple nested context managers # in with statements. if isinstance(node, nodes.With): return # For try... except... finally..., the two nodes # appear to be on the same line due to how the AST is built. if isinstance(node, nodes.TryExcept) and isinstance( node.parent, nodes.TryFinally): return if (isinstance(node.parent, nodes.If) and not node.parent.orelse and self.config.single_line_if_stmt): return if (isinstance(node.parent, nodes.ClassDef) and len(node.parent.body) == 1 and self.config.single_line_class_stmt): return # Function overloads that use ``Ellipsis`` are exempted. if isinstance(node, nodes.Expr) and (isinstance(node.value, nodes.Ellipsis) or (isinstance(node.value, nodes.Const) and node.value.value is Ellipsis)): frame = node.frame() if is_overload_stub(frame) or is_protocol_class( node_frame_class(frame)): return self.add_message("multiple-statements", node=node) self._visited_lines[line] = 2
def leave_functiondef(self, node: nodes.FunctionDef) -> None: """On method node, check if this method couldn't be a function. ignore class, static and abstract methods, initializer, methods overridden from a parent class. """ if node.is_method(): first = self._first_attrs.pop() if first is None: return class_node = node.parent.frame(future=True) if ( self._meth_could_be_func and node.type == "method" and node.name not in PYMETHODS and not ( node.is_abstract() or overrides_a_method(class_node, node.name) or decorated_with_property(node) or _has_bare_super_call(node) or is_protocol_class(class_node) or is_overload_stub(node) ) ): self.add_message("no-self-use", node=node, confidence=INFERENCE)
def visit_functiondef(self, node: nodes.FunctionDef) -> None: if self.linter.namespace.no_docstring_rgx.match(node.name) is None: ftype = "method" if node.is_method() else "function" if (is_property_setter(node) or is_property_deleter(node) or is_overload_stub(node)): return if isinstance(node.parent.frame(future=True), nodes.ClassDef): overridden = False confidence = (interfaces.INFERENCE if utils.has_known_bases( node.parent.frame( future=True)) else interfaces.INFERENCE_FAILURE) # check if node is from a method overridden by its ancestor for ancestor in node.parent.frame(future=True).ancestors(): if ancestor.qname() == "builtins.object": continue if node.name in ancestor and isinstance( ancestor[node.name], nodes.FunctionDef): overridden = True break self._check_docstring( ftype, node, report_missing=not overridden, confidence=confidence # type: ignore[arg-type] ) elif isinstance(node.parent.frame(future=True), nodes.Module): self._check_docstring(ftype, node) # type: ignore[arg-type] else: return
def _check_redefinition(self, redeftype, node): """Check for redefinition of a function / method / class name.""" parent_frame = node.parent.frame(future=True) # Ignore function stubs created for type information redefinitions = [ i for i in parent_frame.locals[node.name] if not (isinstance(i.parent, nodes.AnnAssign) and i.parent.simple) ] defined_self = next( (local for local in redefinitions if not utils.is_overload_stub(local)), node, ) if defined_self is not node and not astroid.are_exclusive( node, defined_self): # Additional checks for methods which are not considered # redefined, since they are already part of the base API. if (isinstance(parent_frame, nodes.ClassDef) and node.name in REDEFINABLE_METHODS): return # Skip typing.overload() functions. if utils.is_overload_stub(node): return # Exempt functions redefined on a condition. if isinstance(node.parent, nodes.If): # Exempt "if not <func>" cases if (isinstance(node.parent.test, nodes.UnaryOp) and node.parent.test.op == "not" and isinstance(node.parent.test.operand, nodes.Name) and node.parent.test.operand.name == node.name): return # Exempt "if <func> is not None" cases # pylint: disable=too-many-boolean-expressions if (isinstance(node.parent.test, nodes.Compare) and isinstance(node.parent.test.left, nodes.Name) and node.parent.test.left.name == node.name and node.parent.test.ops[0][0] == "is" and isinstance(node.parent.test.ops[0][1], nodes.Const) and node.parent.test.ops[0][1].value is None): return # Check if we have forward references for this node. try: redefinition_index = redefinitions.index(node) except ValueError: pass else: for redefinition in redefinitions[:redefinition_index]: inferred = utils.safe_infer(redefinition) if (inferred and isinstance(inferred, astroid.Instance) and inferred.qname() == TYPING_FORWARD_REF_QNAME): return dummy_variables_rgx = lint_utils.get_global_option( self, "dummy-variables-rgx", default=None) if dummy_variables_rgx and dummy_variables_rgx.match(node.name): return self.add_message( "function-redefined", node=node, args=(redeftype, defined_self.fromlineno), )