def visit_function(self, node): """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.CallFunc): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, astroid.Getattr): continue call = expr.expr # skip the test if using super if isinstance(call, astroid.CallFunc) and \ isinstance(call.func, astroid.Name) and \ call.func.name == 'super': confidence = (INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE) if not klass.newstyle: # super should not be used on an old style class self.add_message('super-on-old-class', node=node, confidence=confidence) else: # super first arg should be the class if not call.args and sys.version_info[0] == 3: # unless Python 3 continue try: supcls = (call.args and next(call.args[0].infer()) or None) except astroid.InferenceError: continue if supcls is None: self.add_message('missing-super-argument', node=call, confidence=confidence) continue if klass is not supcls: name = None # if supcls is not YES, then supcls was infered # and use its name. Otherwise, try to look # for call.args[0].name if supcls is not astroid.YES: name = supcls.name else: if hasattr(call.args[0], 'name'): name = call.args[0].name if name is not None: self.add_message('bad-super-call', node=call, args=(name, ), confidence=confidence)
def visit_functiondef(self, node): """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, astroid.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, astroid.Call) and isinstance(call.func, astroid.Name) and call.func.name == 'super'): continue confidence = (INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE) if not klass.newstyle: # super should not be used on an old style class self.add_message('super-on-old-class', node=node, confidence=confidence) else: # super first arg should be the class if not call.args and sys.version_info[0] == 3: # unless Python 3 continue try: supcls = (call.args and next(call.args[0].infer()) or None) except astroid.InferenceError: continue if supcls is None: self.add_message('missing-super-argument', node=call, confidence=confidence) continue if klass is not supcls: name = None # if supcls is not YES, then supcls was infered # and use its name. Otherwise, try to look # for call.args[0].name if supcls is not astroid.YES: name = supcls.name else: if hasattr(call.args[0], 'name'): name = call.args[0].name if name is not None: self.add_message('bad-super-call', node=call, args=(name, ), confidence=confidence)
def check_functiondef_params(self, node, node_doc): """Checks whether all parameters in a function definition are documented. Args: node: astroid.scoped_nodes.Function. Node for a function or method definition in the AST. node_doc: Docstring. Pylint Docstring class instance representing a node's docstring. """ node_allow_no_param = None if node.name in self.constructor_names: class_node = checker_utils.node_frame_class(node) if class_node is not None: class_doc = docstrings_checker.docstringify(class_node.doc) self.check_single_constructor_params(class_doc, node_doc, class_node) # __init__ or class docstrings can have no parameters documented # as long as the other documents them. node_allow_no_param = (class_doc.has_params() or class_doc.params_documented_elsewhere() or None) class_allow_no_param = (node_doc.has_params() or node_doc.params_documented_elsewhere() or None) self.check_arguments_in_docstring( class_doc, node.args, class_node, accept_no_param_doc=class_allow_no_param) self.check_arguments_in_docstring( node_doc, node.args, node, accept_no_param_doc=node_allow_no_param)
def _check_protected_attribute_access(self, node): '''Given an attribute access node (set or get), check if attribute access is legitimate. Call _check_first_attr with node before calling this method. Valid cases are: * self._attr in a method or cls._attr in a classmethod. Checked by _check_first_attr. * Klass._attr inside "Klass" class. * Klass2._attr inside "Klass" class when Klass2 is a base class of Klass. ''' attrname = node.attrname if is_attr_protected(attrname): klass = node_frame_class(node) # XXX infer to be more safe and less dirty ?? # in classes, check we are not getting a parent method # through the class object or through super callee = node.expr.as_string() # We are not in a class, no remaining valid case if klass is None: self.add_message('W0212', node=node, args=attrname) return # We are in a class, one remaining valid cases, Klass._attr inside # Klass if not (callee == klass.name or callee in klass.basenames): self.add_message('W0212', node=node, args=attrname)
def visit_functiondef(self, node): """Called for function and method definitions (def). :param node: Node for a function or method definition in the AST :type node: :class:`astroid.scoped_nodes.Function` """ node_allow_no_param = None node_doc = utils.docstringify(node.doc) if node.name in self.constructor_names: class_node = node_frame_class(node) if class_node is not None: class_doc = utils.docstringify(class_node.doc) self.check_single_constructor_params(class_doc, node_doc, class_node) # __init__ or class docstrings can have no parameters documented # as long as the other documents them. node_allow_no_param = class_doc.has_params() or None class_allow_no_param = node_doc.has_params() or None self.check_arguments_in_docstring(class_doc, node.args, class_node, class_allow_no_param) self.check_arguments_in_docstring(node_doc, node.args, node, node_allow_no_param)
def _check(self, node): attrname = node.attrname klass = node_frame_class(node) if klass is None or ( attrname not in klass.instance_attrs and attrname not in (m.name for m in klass.methods())): old_check_protected_attribute_access(self, node)
def check_functiondef_params(self, node, node_doc): node_allow_no_param = None if node.name in self.constructor_names: class_node = checker_utils.node_frame_class(node) if class_node is not None: class_doc = utils.docstringify( class_node.doc, self.config.default_docstring_type, ) self.check_single_constructor_params(class_doc, node_doc, class_node) # __init__ or class docstrings can have no parameters documented # as long as the other documents them. node_allow_no_param = ( class_doc.has_params() or class_doc.params_documented_elsewhere() or None ) class_allow_no_param = ( node_doc.has_params() or node_doc.params_documented_elsewhere() or None ) self.check_arguments_in_docstring( class_doc, node.args, class_node, class_allow_no_param) self.check_arguments_in_docstring( node_doc, node.args, node, node_allow_no_param)
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 visit_functiondef(self, node): """Called for function and method definitions (def). :param node: Node for a function or method definition in the AST :type node: :class:`astroid.scoped_nodes.Function` """ node_allow_no_param = None node_doc = utils.docstringify(node.doc) if node.name in self.constructor_names: class_node = node_frame_class(node) if class_node is not None: class_doc = utils.docstringify(class_node.doc) self.check_single_constructor_params(class_doc, node_doc, class_node) # __init__ or class docstrings can have no parameters documented # as long as the other documents them. node_allow_no_param = class_doc.has_params() or None class_allow_no_param = node_doc.has_params() or None self.check_arguments_in_docstring( class_doc, node.args, class_node, class_allow_no_param) self.check_arguments_in_docstring( node_doc, node.args, node, node_allow_no_param)
def check_functiondef_params(self, node, node_doc): node_allow_no_param = None if node.name in self.constructor_names: class_node = checker_utils.node_frame_class(node) if class_node is not None: class_doc = utils.docstringify( class_node.doc, self.config.default_docstring_type) self.check_single_constructor_params(class_doc, node_doc, class_node) # __init__ or class docstrings can have no parameters documented # as long as the other documents them. node_allow_no_param = (class_doc.has_params() or class_doc.params_documented_elsewhere() or None) class_allow_no_param = (node_doc.has_params() or node_doc.params_documented_elsewhere() or None) self.check_arguments_in_docstring(class_doc, node.args, class_node, class_allow_no_param) self.check_arguments_in_docstring(node_doc, node.args, node, node_allow_no_param)
def _check_protected_attribute_access(self, node): '''Given an attribute access node (set or get), check if attribute access is legitimate. Call _check_first_attr with node before calling this method. Valid cases are: * self._attr in a method or cls._attr in a classmethod. Checked by _check_first_attr. * Klass._attr inside "Klass" class. * Klass2._attr inside "Klass" class when Klass2 is a base class of Klass. ''' attrname = node.attrname if (is_attr_protected(attrname) and attrname not in self.config.exclude_protected): klass = node_frame_class(node) # XXX infer to be more safe and less dirty ?? # in classes, check we are not getting a parent method # through the class object or through super callee = node.expr.as_string() # We are not in a class, no remaining valid case if klass is None: self.add_message('protected-access', node=node, args=attrname) return # If the expression begins with a call to super, that's ok. if isinstance(node.expr, astroid.CallFunc) and \ isinstance(node.expr.func, astroid.Name) and \ node.expr.func.name == 'super': return # We are in a class, one remaining valid cases, Klass._attr inside # Klass if not (callee == klass.name or callee in klass.basenames): # Detect property assignments in the body of the class. # This is acceptable: # # class A: # b = property(lambda: self._b) stmt = node.parent.statement() try: if (isinstance(stmt, astroid.Assign) and (stmt in klass.body or klass.parent_of(stmt)) and isinstance(stmt.value, astroid.CallFunc) and isinstance(stmt.value.func, astroid.Name) and stmt.value.func.name == 'property' and is_builtin_object( next(stmt.value.func.infer(), None))): return except astroid.InferenceError: pass self.add_message('protected-access', node=node, args=attrname)
def _check_init(self, node): """check that the __init__ method call super or ancestors'__init__ method """ if (not self.linter.is_message_enabled('super-init-not-called') and not self.linter.is_message_enabled('non-parent-init-called')): return klass_node = node.parent.frame() to_call = _ancestors_to_call(klass_node) not_called_yet = dict(to_call) for stmt in node.nodes_of_class(astroid.Call): expr = stmt.func if not isinstance(expr, astroid.Attribute) \ or expr.attrname != '__init__': continue # skip the test if using super if isinstance(expr.expr, astroid.Call) and \ isinstance(expr.expr.func, astroid.Name) and \ expr.expr.func.name == 'super': return try: for klass in expr.expr.infer(): if klass is astroid.YES: continue # The infered klass can be super(), which was # assigned to a variable and the `__init__` # was called later. # # base = super() # base.__init__(...) if (isinstance(klass, astroid.Instance) and isinstance(klass._proxied, astroid.ClassDef) and is_builtin_object(klass._proxied) and klass._proxied.name == 'super'): return elif isinstance(klass, objects.Super): return try: del not_called_yet[klass] except KeyError: if klass not in to_call: self.add_message('non-parent-init-called', node=expr, args=klass.name) except astroid.InferenceError: continue for klass, method in six.iteritems(not_called_yet): cls = node_frame_class(method) if klass.name == 'object' or (cls and cls.name == 'object'): continue self.add_message('super-init-not-called', args=klass.name, node=node)
def _check_protected_attribute_access(self, node): '''Given an attribute access node (set or get), check if attribute access is legitimate. Call _check_first_attr with node before calling this method. Valid cases are: * self._attr in a method or cls._attr in a classmethod. Checked by _check_first_attr. * Klass._attr inside "Klass" class. * Klass2._attr inside "Klass" class when Klass2 is a base class of Klass. ''' attrname = node.attrname if (is_attr_protected(attrname) and attrname not in self.config.exclude_protected): klass = node_frame_class(node) # XXX infer to be more safe and less dirty ?? # in classes, check we are not getting a parent method # through the class object or through super callee = node.expr.as_string() # We are not in a class, no remaining valid case if klass is None: self.add_message('protected-access', node=node, args=attrname) return # If the expression begins with a call to super, that's ok. if isinstance(node.expr, astroid.CallFunc) and \ isinstance(node.expr.func, astroid.Name) and \ node.expr.func.name == 'super': return # We are in a class, one remaining valid cases, Klass._attr inside # Klass if not (callee == klass.name or callee in klass.basenames): # Detect property assignments in the body of the class. # This is acceptable: # # class A: # b = property(lambda: self._b) stmt = node.parent.statement() try: if (isinstance(stmt, astroid.Assign) and (stmt in klass.body or klass.parent_of(stmt)) and isinstance(stmt.value, astroid.CallFunc) and isinstance(stmt.value.func, astroid.Name) and stmt.value.func.name == 'property' and is_builtin_object(next(stmt.value.func.infer(), None))): return except astroid.InferenceError: pass self.add_message('protected-access', node=node, args=attrname)
def visit_functiondef(self, node): """Called for function and method definitions (def). :param node: Node for a function or method definition in the AST :type node: :class:`astroid.scoped_nodes.Function` """ if node.name in self.constructor_names: class_node = node_frame_class(node) if class_node is not None: self.check_arguments_in_docstring( class_node.doc, node.args, class_node) return self.check_arguments_in_docstring(node.doc, node.args, node)
def visit_functiondef(self, node): """Called for function and method definitions (def). :param node: Node for a function or method definition in the AST :type node: :class:`astroid.scoped_nodes.Function` """ if node.name in self.constructor_names: class_node = node_frame_class(node) if class_node is not None: self.check_arguments_in_docstring(class_node.doc, node.args, class_node) return self.check_arguments_in_docstring(node.doc, node.args, node)
def get_setters_property(node): """Get the property node for the given setter node. :param node: The node to get the property for. :type node: astroid.FunctionDef :rtype: astroid.FunctionDef or None :returns: The node relating to the property of the given setter node, or None if one could not be found. """ property_ = None property_name = get_setters_property_name(node) class_node = utils.node_frame_class(node) if property_name and class_node: class_attrs = class_node.getattr(node.name) for attr in class_attrs: if utils.decorated_with_property(attr): property_ = attr break return property_
def get_setters_property(node): """Get the property node for the given setter node. :param node: The node to get the property for. :type node: nodes.FunctionDef :rtype: nodes.FunctionDef or None :returns: The node relating to the property of the given setter node, or None if one could not be found. """ property_ = None property_name = get_setters_property_name(node) class_node = utils.node_frame_class(node) if property_name and class_node: class_attrs = class_node.getattr(node.name) for attr in class_attrs: if utils.decorated_with_property(attr): property_ = attr break return property_
def get_setters_property(node): """Get the property node for the given setter node. Args: node: astroid.FunctionDef. The node to get the property for. Returns: astroid.FunctionDef|None. The node relating to the property of the given setter node, or None if one could not be found. """ setters_property = None property_name = get_setters_property_name(node) class_node = utils.node_frame_class(node) if property_name and class_node: class_attrs = class_node.getattr(node.name) for attr in class_attrs: if utils.decorated_with_property(attr): setters_property = attr break return setters_property
def _check_protected_attribute_access(self, node): """Given an attribute access node (set or get), check if attribute access is legitimate. Call _check_first_attr with node before calling this method. Valid cases are: * self._attr in a method or cls._attr in a classmethod. Checked by _check_first_attr. * Klass._attr inside "Klass" class. * Klass2._attr inside "Klass" class when Klass2 is a base class of Klass. """ attrname = node.attrname if is_attr_protected(attrname): klass = node_frame_class(node) # XXX infer to be more safe and less dirty ?? # in classes, check we are not getting a parent method # through the class object or through super callee = node.expr.as_string() # We are not in a class, no remaining valid case if klass is None: self.add_message("protected-access", node=node, args=attrname) return # If the expression begins with a call to super, that's ok. if ( isinstance(node.expr, astroid.CallFunc) and isinstance(node.expr.func, astroid.Name) and node.expr.func.name == "super" ): return # We are in a class, one remaining valid cases, Klass._attr inside # Klass if not (callee == klass.name or callee in klass.basenames): self.add_message("protected-access", node=node, args=attrname)
def _check_inferred_class_is_abstract(self, inferred, node): if not isinstance(inferred, nodes.ClassDef): return klass = utils.node_frame_class(node) if klass is inferred: # Don't emit the warning if the class is instantiated # in its own body or if the call is not an instance # creation. If the class is instantiated into its own # body, we're expecting that it knows what it is doing. return # __init__ was called abstract_methods = _has_abstract_methods(inferred) if not abstract_methods: return metaclass = inferred.metaclass() if metaclass is None: # Python 3.4 has `abc.ABC`, which won't be detected # by ClassNode.metaclass() for ancestor in inferred.ancestors(): if ancestor.qname() == "abc.ABC": self.add_message("abstract-class-instantiated", args=(inferred.name, ), node=node) break return if metaclass.qname() in ABC_METACLASSES: self.add_message("abstract-class-instantiated", args=(inferred.name, ), node=node)
def _check(self, node): attrname = node.attrname klass = node_frame_class(node) if klass is None or attrname not in klass.instance_attrs: old_check_protected_attribute_access(self, node)
def visit_functiondef(self, node): """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, astroid.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, astroid.Call) and isinstance(call.func, astroid.Name) and call.func.name == 'super'): continue if not klass.newstyle and has_known_bases(klass): # super should not be used on an old style class self.add_message('super-on-old-class', node=node) else: # super first arg should be the class if not call.args: if sys.version_info[0] == 3: # unless Python 3 continue else: self.add_message('missing-super-argument', node=call) continue # calling super(type(self), self) can lead to recursion loop # in derived classes arg0 = call.args[0] if isinstance(arg0, astroid.Call) and \ isinstance(arg0.func, astroid.Name) and \ arg0.func.name == 'type': self.add_message('bad-super-call', node=call, args=('type', )) continue # calling super(self.__class__, self) can lead to recursion loop # in derived classes if len(call.args) >= 2 and \ isinstance(call.args[1], astroid.Name) and \ call.args[1].name == 'self' and \ isinstance(arg0, astroid.Attribute) and \ arg0.attrname == '__class__': self.add_message('bad-super-call', node=call, args=('self.__class__', )) continue try: supcls = call.args and next(call.args[0].infer(), None) except astroid.InferenceError: continue if klass is not supcls: name = None # if supcls is not Uninferable, then supcls was infered # and use its name. Otherwise, try to look # for call.args[0].name if supcls: name = supcls.name elif call.args and hasattr(call.args[0], 'name'): name = call.args[0].name if name: self.add_message('bad-super-call', node=call, args=(name, ))
def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(nodes.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, nodes.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, nodes.Call) and isinstance( call.func, nodes.Name) and call.func.name == "super"): continue # super should not be used on an old style class if klass.newstyle or not has_known_bases(klass): # super first arg should not be the class if not call.args: continue # calling super(type(self), self) can lead to recursion loop # in derived classes arg0 = call.args[0] if (isinstance(arg0, nodes.Call) and isinstance(arg0.func, nodes.Name) and arg0.func.name == "type"): self.add_message("bad-super-call", node=call, args=("type", )) continue # calling super(self.__class__, self) can lead to recursion loop # in derived classes if (len(call.args) >= 2 and isinstance(call.args[1], nodes.Name) and call.args[1].name == "self" and isinstance(arg0, nodes.Attribute) and arg0.attrname == "__class__"): self.add_message("bad-super-call", node=call, args=("self.__class__", )) continue try: supcls = call.args and next(call.args[0].infer(), None) except astroid.InferenceError: continue if klass is not supcls: name = None # if supcls is not Uninferable, then supcls was inferred # and use its name. Otherwise, try to look # for call.args[0].name if supcls: name = supcls.name elif call.args and hasattr(call.args[0], "name"): name = call.args[0].name if name: self.add_message("bad-super-call", node=call, args=(name, ))