Beispiel #1
0
    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)
Beispiel #2
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
 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)
Beispiel #8
0
    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)
Beispiel #9
0
    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
Beispiel #10
0
    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)
Beispiel #11
0
    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)
Beispiel #12
0
    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)
Beispiel #13
0
    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)
Beispiel #14
0
    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)
Beispiel #15
0
    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)
Beispiel #16
0
    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)
Beispiel #17
0
    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 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_
Beispiel #19
0
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_
Beispiel #20
0
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
Beispiel #21
0
    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)
Beispiel #22
0
    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)
Beispiel #23
0
 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)
Beispiel #24
0
    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, ))
Beispiel #25
0
    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, ))