Example #1
0
    def _check_cmp_argument(self, node):
        # Check that the `cmp` argument is used
        kwargs = []
        if (isinstance(node.func, astroid.Attribute)
                and node.func.attrname == 'sort'):
            inferred = utils.safe_infer(node.func.expr)
            if not inferred:
                return

            builtins_list = "{}.list".format(bases.BUILTINS)
            if (isinstance(inferred, astroid.List)
                    or inferred.qname() == builtins_list):
                kwargs = node.keywords

        elif (isinstance(node.func, astroid.Name)
              and node.func.name == 'sorted'):
            inferred = utils.safe_infer(node.func)
            if not inferred:
                return

            builtins_sorted = "{}.sorted".format(bases.BUILTINS)
            if inferred.qname() == builtins_sorted:
                kwargs = node.keywords

        for kwarg in kwargs or []:
            if kwarg.arg == 'cmp':
                self.add_message('using-cmp-argument', node=node)
                return
Example #2
0
    def get_constant_values(self, node, key):
        try:
            ass = node.locals[key][-1]
        except KeyError:
            return

        try:
            xs = safe_infer(ass).get_children()
        except AttributeError:
            return

        return [(x, x.value) for x in xs if isinstance(safe_infer(x), astng.Const)]
Example #3
0
 def visit_callfunc(self, node):
     func = utils.safe_infer(node.func)
     if (isinstance(func, astroid.BoundMethod)
         and isinstance(func.bound, astroid.Instance)
         and func.bound.name in ('str', 'unicode', 'bytes')
         and func.name in ('strip', 'lstrip', 'rstrip')
         and node.args):
         arg = utils.safe_infer(node.args[0])
         if not isinstance(arg, astroid.Const):
             return
         if len(arg.value) != len(set(arg.value)):
             self.add_message('bad-str-strip-call', node=node,
                              args=(func.bound.name, func.name))
Example #4
0
 def visit_callfunc(self, node):
     func = utils.safe_infer(node.func)
     if (
         isinstance(func, astroid.BoundMethod)
         and isinstance(func.bound, astroid.Instance)
         and func.bound.name in ("str", "unicode", "bytes")
         and func.name in ("strip", "lstrip", "rstrip")
         and node.args
     ):
         arg = utils.safe_infer(node.args[0])
         if not isinstance(arg, astroid.Const):
             return
         if len(arg.value) != len(set(arg.value)):
             self.add_message("E1310", node=node, args=(func.bound.name, func.name))
def possible_exc_types(node):
    """
    Gets all of the possible raised exception types for the given raise node.

    .. note::

        Caught exception types are ignored.


    :param node: The raise node to find exception types for.
    :type node: astroid.node_classes.NodeNG

    :returns: A list of exception types possibly raised by :param:`node`.
    :rtype: list(str)
    """
    excs = []
    if isinstance(node.exc, astroid.Name):
        inferred = safe_infer(node.exc)
        if inferred:
            excs = [inferred.name]
    elif (isinstance(node.exc, astroid.Call) and
          isinstance(node.exc.func, astroid.Name)):
        target = safe_infer(node.exc.func)
        if isinstance(target, astroid.ClassDef):
            excs = [target.name]
        elif isinstance(target, astroid.FunctionDef):
            for ret in target.nodes_of_class(astroid.Return):
                if ret.frame() != target:
                    # return from inner function - ignore it
                    continue

                val = safe_infer(ret.value)
                if (val and isinstance(val, (astroid.Instance, astroid.ClassDef))
                        and inherit_from_std_ex(val)):
                    excs.append(val.name)
    elif node.exc is None:
        handler = node.parent
        while handler and not isinstance(handler, astroid.ExceptHandler):
            handler = handler.parent

        if handler and handler.type:
            inferred_excs = astroid.unpack_infer(handler.type)
            excs = (exc.name for exc in inferred_excs
                    if exc is not astroid.Uninferable)


    try:
        return set(exc for exc in excs if not node_ignores_exception(node, exc))
    except astroid.InferenceError:
        return ()
    def _check_try_except_raise(self, node):
        def gather_exceptions_from_handler(handler):
            exceptions = []
            if handler.type:
                exceptions_in_handler = utils.safe_infer(handler.type)
                if isinstance(exceptions_in_handler, astroid.Tuple):
                    exceptions = {
                        exception
                        for exception in exceptions_in_handler.elts
                        if isinstance(exception, astroid.Name)
                    }
                elif exceptions_in_handler:
                    exceptions = [exceptions_in_handler]
            return exceptions

        bare_raise = False
        handler_having_bare_raise = None
        excs_in_bare_handler = []
        for handler in node.handlers:
            if bare_raise:
                # check that subsequent handler is not parent of handler which had bare raise.
                # since utils.safe_infer can fail for bare except, check it before.
                # also break early if bare except is followed by bare except.

                excs_in_current_handler = gather_exceptions_from_handler(handler)
                if not excs_in_current_handler:
                    bare_raise = False
                    break

                for exc_in_current_handler in excs_in_current_handler:
                    inferred_current = utils.safe_infer(exc_in_current_handler)
                    if any(
                        utils.is_subclass_of(
                            utils.safe_infer(exc_in_bare_handler), inferred_current
                        )
                        for exc_in_bare_handler in excs_in_bare_handler
                    ):
                        bare_raise = False
                        break

            # `raise` as the first operator inside the except handler
            if _is_raising([handler.body[0]]):
                # flags when there is a bare raise
                if handler.body[0].exc is None:
                    bare_raise = True
                    handler_having_bare_raise = handler
                    excs_in_bare_handler = gather_exceptions_from_handler(handler)
        if bare_raise:
            self.add_message("try-except-raise", node=handler_having_bare_raise)
Example #7
0
 def visit_call(self, node):
     """Visit a Call node."""
     if hasattr(node, "func"):
         infer = utils.safe_infer(node.func)
         if infer and infer.root().name == "qutebrowser.config.config":
             if getattr(node.func, "attrname", None) in ("get", "set"):
                 self._check_config(node)
Example #8
0
def nodeisinstance(node, klasses, check_base_classes=True):
    if not isinstance(node, astng.Class):
        return False

    for base in node.bases:
        val = safe_infer(base)
        if not val:
            continue

        if type(val).__name__ == '_Yes':
            continue

        nodes = [val]
        if check_base_classes:
            try:
                nodes = chain([val], val.ancestors())
            except TypeError:
                pass

        for node in nodes:
            qual = '%s.%s' % (node.root().name, node.name)
            if qual in klasses:
                return True

    return False
Example #9
0
    def visit_callfunc(self, node):
        func = utils.safe_infer(node.func)
        if isinstance(func, astroid.BoundMethod) and func.name == "format":
            # If there's a .format() call, run the code below

            if isinstance(node.func.expr, (astroid.Name, astroid.Const)):
                # This is for:
                #   foo = 'Foo {} bar'
                #   print(foo.format(blah)
                for inferred in node.func.expr.infer():
                    if not hasattr(inferred, "value"):
                        # If there's no value attribute, it's not worth
                        # checking.
                        continue

                    if BAD_FORMATTING_SLOT.findall(inferred.value):
                        if self.config.un_indexed_curly_braces_always_error or sys.version_info[:2] < (2, 7):
                            msgid = "E1320"
                        else:
                            msgid = "W1320"
                        self.add_message(msgid, node=inferred, args=inferred.value)
            elif not hasattr(node.func.expr, "value"):
                # If it does not have an value attribute, it's not worth
                # checking
                return
            elif isinstance(node.func.expr.value, astroid.Name):
                # No need to check these either
                return
            elif BAD_FORMATTING_SLOT.findall(node.func.expr.value):
                if self.config.un_indexed_curly_braces_always_error or sys.version_info[:2] < (2, 7):
                    msgid = "E1320"
                else:
                    msgid = "W1320"
                self.add_message("E1320", node=node, args=node.func.expr.value)
Example #10
0
 def visit_raise(self, node):
     """visit raise possibly inferring value"""
     # ignore empty raise
     if node.exc is None:
         return
     if PY3K and node.cause:
         cause = safe_infer(node.cause)
         if cause is YES or cause is None:
             return
         if isinstance(cause, astroid.Const):
             if cause.value is not None:
                 self.add_message('bad-exception-context',
                                  node=node)
         elif (not isinstance(cause, astroid.Class) and
               not inherit_from_std_ex(cause)):
             self.add_message('bad-exception-context',
                              node=node)
     expr = node.exc
     if self._check_raise_value(node, expr):
         return
     else:
         try:
             value = next(unpack_infer(expr))
         except astroid.InferenceError:
             return
         self._check_raise_value(node, value)
Example #11
0
def _determine_function_name_type(node):
    """Determine the name type whose regex the a function's name should match.

    :param node: A function node.
    :returns: One of ('function', 'method', 'attr')
    """
    if not node.is_method():
        return 'function'
    if node.decorators:
        decorators = node.decorators.nodes
    else:
        decorators = []
    for decorator in decorators:
        # If the function is a property (decorated with @property
        # or @abc.abstractproperty), the name type is 'attr'.
        if (isinstance(decorator, astroid.Name) or
            (isinstance(decorator, astroid.Getattr) and
             decorator.attrname == 'abstractproperty')):
            infered = safe_infer(decorator)
            if infered and infered.qname() in PROPERTY_CLASSES:
                return 'attr'
        # If the function is decorated using the prop_method.{setter,getter}
        # form, treat it like an attribute as well.
        elif (isinstance(decorator, astroid.Getattr) and
              decorator.attrname in ('setter', 'deleter')):
            return 'attr'
    return 'method'
Example #12
0
    def _check_catching_non_exception(self, handler, exc, part):
        if isinstance(exc, astroid.Tuple):
            # Check if it is a tuple of exceptions.
            inferred = [safe_infer(elt) for elt in exc.elts]
            if any(node is astroid.YES for node in inferred):
                # Don't emit if we don't know every component.
                return
            if all(node and inherit_from_std_ex(node) for node in inferred):
                return

        if not isinstance(exc, astroid.ClassDef):
            # Don't emit the warning if the infered stmt
            # is None, but the exception handler is something else,
            # maybe it was redefined.
            if isinstance(exc, astroid.Const) and exc.value is None:
                if (isinstance(handler.type, astroid.Const) and handler.type.value is None) or handler.type.parent_of(
                    exc
                ):
                    # If the exception handler catches None or
                    # the exception component, which is None, is
                    # defined by the entire exception handler, then
                    # emit a warning.
                    self.add_message("catching-non-exception", node=handler.type, args=(part.as_string(),))
            else:
                self.add_message("catching-non-exception", node=handler.type, args=(part.as_string(),))
            return

        if not inherit_from_std_ex(exc) and exc.name not in self.builtin_exceptions:
            if has_known_bases(exc):
                self.add_message("catching-non-exception", node=handler.type, args=(exc.name,))
Example #13
0
    def _check_raising_stopiteration_in_generator_next_call(self, node):
        """Check if a StopIteration exception is raised by the call to next function

        If the next value has a default value, then do not add message.

        :param node: Check to see if this Call node is a next function
        :type node: :class:`astroid.node_classes.Call`
        """

        def _looks_like_infinite_iterator(param):
            inferred = utils.safe_infer(param)
            if inferred is not None or inferred is not astroid.Uninferable:
                return inferred.qname() in KNOWN_INFINITE_ITERATORS
            return False

        inferred = utils.safe_infer(node.func)
        if getattr(inferred, 'name', '') == 'next':
            frame = node.frame()
            # The next builtin can only have up to two
            # positional arguments and no keyword arguments
            has_sentinel_value = len(node.args) > 1
            if (isinstance(frame, astroid.FunctionDef)
                    and frame.is_generator()
                    and not has_sentinel_value
                    and not utils.node_ignores_exception(node, StopIteration)
                    and not _looks_like_infinite_iterator(node.args[0])):
                self.add_message('stop-iteration-return', node=node)
Example #14
0
    def _check_in_slots(self, node):
        """ Check that the given assattr node
        is defined in the class slots.
        """
        infered = safe_infer(node.expr)
        if infered and isinstance(infered, Instance):
            klass = infered._proxied
            if '__slots__' not in klass.locals or not klass.newstyle:
                return

            slots = klass.slots()
            if slots is None:
                return
            # If any ancestor doesn't use slots, the slots
            # defined for this class are superfluous.
            if any('__slots__' not in ancestor.locals and
                   ancestor.name != 'object'
                   for ancestor in klass.ancestors()):
                return

            if not any(slot.value == node.attrname for slot in slots):
                # If we have a '__dict__' in slots, then
                # assigning any name is valid.
                if not any(slot.value == '__dict__' for slot in slots):
                    if _is_attribute_property(node.attrname, klass):
                        # Properties circumvent the slots mechanism,
                        # so we should not emit a warning for them.
                        return
                    self.add_message('assigning-non-slot',
                                     args=(node.attrname, ), node=node)
Example #15
0
    def _check_consider_get(self, node):
        def type_and_name_are_equal(node_a, node_b):
            for _type in [astroid.Name, astroid.AssignName]:
                if all(isinstance(_node, _type) for _node in [node_a, node_b]):
                    return node_a.name == node_b.name
            if all(isinstance(_node, astroid.Const) for _node in [node_a, node_b]):
                return node_a.value == node_b.value
            return False

        if_block_ok = (
            isinstance(node.test, astroid.Compare)
            and len(node.body) == 1
            and isinstance(node.body[0], astroid.Assign)
            and isinstance(node.body[0].value, astroid.Subscript)
            and type_and_name_are_equal(node.body[0].value.value, node.test.ops[0][1])
            and type_and_name_are_equal(node.body[0].value.slice.value, node.test.left)
            and len(node.body[0].targets) == 1
            and isinstance(utils.safe_infer(node.test.ops[0][1]), astroid.Dict))

        if if_block_ok and not node.orelse:
            self.add_message('consider-using-get', node=node)
        elif (if_block_ok and len(node.orelse) == 1
              and isinstance(node.orelse[0], astroid.Assign)
              and type_and_name_are_equal(node.orelse[0].targets[0], node.body[0].targets[0])
              and len(node.orelse[0].targets) == 1):
            self.add_message('consider-using-get', node=node)
Example #16
0
 def _check_open_encoding(self, node):
     """Check that an open() call always has an encoding set."""
     try:
         mode_arg = utils.get_argument_from_call(node, position=1,
                                                 keyword='mode')
     except utils.NoSuchArgumentError:
         mode_arg = None
     _encoding = None
     try:
         _encoding = utils.get_argument_from_call(node, position=2)
     except utils.NoSuchArgumentError:
         try:
             _encoding = utils.get_argument_from_call(node,
                                                      keyword='encoding')
         except utils.NoSuchArgumentError:
             pass
     if _encoding is None:
         if mode_arg is not None:
             mode = utils.safe_infer(mode_arg)
         if (mode_arg is not None and isinstance(mode, astroid.Const) and
                 'b' in getattr(mode, 'value', '')):
             # Files opened as binary don't need an encoding.
             return
         else:
             self.add_message('open-without-encoding', node=node)
Example #17
0
 def visit_call(self, node):
     """Visit a Call node."""
     if hasattr(node, 'func'):
         infer = utils.safe_infer(node.func)
         if infer:
             if getattr(node.func, 'name', None) == 'set_trace':
                 self.add_message('set-trace', node=node)
Example #18
0
 def visit_callfunc(self, node):
     inferred = safe_infer(node.func)
     if inferred is not None:
         if isinstance(inferred, astng.Function):
             key = (inferred.parent.frame().qname(), inferred.name)
             # Find unqualified function calls
             if not isinstance(node.func, astng.Getattr) and key in self.BAD_FUNCTIONS:
                 self.add_message(self.BAD_FUNCTIONS[key], line=node.lineno)
             if node.args is not None:
                 for arg in node.args:
                     if isinstance(arg, astng.Keyword):
                         newkey = key + (arg.arg,)
                         if newkey in self.BAD_ARGUMENTS:
                             self.add_message(self.BAD_ARGUMENTS[newkey], line=node.lineno)
         elif isinstance(inferred, astng.Class):
             key = (inferred.parent.frame().qname(), inferred.name)
             if key in self.BAD_CLASSES:
                 self.add_message(self.BAD_CLASSES[key], line=node.lineno)
             if key == ('operator', 'itemgetter'):
                 children = list(node.get_children())
                 if len(children) > 2:
                     self.add_message('W8407', line=node.lineno)
             elif key == ('operator', 'attrgetter'):
                 children = list(node.get_children())
                 if len(children) > 2:
                     self.add_message('W8408', line=node.lineno)
         elif isinstance(inferred, astng.UnboundMethod):
             key = (inferred.parent.frame().qname(), inferred.name)
             if key == ('__builtin__.str', 'startswith'):
                 if isinstance(node.args[0], astng.Tuple):
                     self.add_message('W8405', line=node.lineno)
             elif key == ('__builtin__.str', 'endswith'):
                 if isinstance(node.args[0], astng.Tuple):
                     self.add_message('W8406', line=node.lineno)
Example #19
0
    def _check_log_method(self, node, name):
        """Checks calls to logging.log(level, format, *format_args)."""
        if name == 'log':
            if node.starargs or node.kwargs or len(node.args) < 2:
                # Either a malformed call, star args, or double-star args. Beyond
                # the scope of this checker.
                return
            format_pos = 1
        elif name in CHECKED_CONVENIENCE_FUNCTIONS:
            if node.starargs or node.kwargs or not node.args:
                # Either no args, star args, or double-star args. Beyond the
                # scope of this checker.
                return
            format_pos = 0
        else:
            return

        if isinstance(node.args[format_pos], astroid.BinOp):
            binop = node.args[format_pos]
            emit = binop.op == '%'
            if binop.op == '+':
                total_number_of_strings = sum(
                    1 for operand in (binop.left, binop.right)
                    if self._is_operand_literal_str(utils.safe_infer(operand))
                )
                emit = total_number_of_strings > 0
            if emit:
                self.add_message('logging-not-lazy', node=node)
        elif isinstance(node.args[format_pos], astroid.Call):
            self._check_call_func(node.args[format_pos])
        elif isinstance(node.args[format_pos], astroid.Const):
            self._check_format_string(node, format_pos)
        elif isinstance(node.args[format_pos], (astroid.FormattedValue, astroid.JoinedStr)):
            self.add_message('logging-fstring-interpolation', node=node)
Example #20
0
    def visit_asyncwith(self, node):
        for ctx_mgr, _ in node.items:
            inferred = checker_utils.safe_infer(ctx_mgr)
            if inferred is None or inferred is astroid.Uninferable:
                continue

            if isinstance(inferred, bases.AsyncGenerator):
                # Check if we are dealing with a function decorated
                # with contextlib.asynccontextmanager.
                if decorated_with(inferred.parent, self._async_generators):
                    continue
            else:
                try:
                    inferred.getattr("__aenter__")
                    inferred.getattr("__aexit__")
                except exceptions.NotFoundError:
                    if isinstance(inferred, astroid.Instance):
                        # If we do not know the bases of this class,
                        # just skip it.
                        if not checker_utils.has_known_bases(inferred):
                            continue
                        # Just ignore mixin classes.
                        if self._ignore_mixin_members:
                            if inferred.name[-5:].lower() == "mixin":
                                continue
                else:
                    continue

            self.add_message(
                "not-async-context-manager", node=node, args=(inferred.name,)
            )
Example #21
0
 def visit_assign(self, node):
     """check that if assigning to a function call, the function is
     possibly returning something valuable
     """
     if not isinstance(node.value, astroid.CallFunc):
         return
     function_node = safe_infer(node.value.func)
     # skip class, generator and incomplete function definition
     if not (isinstance(function_node, astroid.Function) and
             function_node.root().fully_defined()):
         return
     if function_node.is_generator() \
            or function_node.is_abstract(pass_is_abstract=False):
         return
     returns = list(function_node.nodes_of_class(astroid.Return,
                                                 skip_klass=astroid.Function))
     if len(returns) == 0:
         self.add_message('assignment-from-no-return', node=node)
     else:
         for rnode in returns:
             if not (isinstance(rnode.value, astroid.Const)
                     and rnode.value.value is None
                     or rnode.value is None):
                 break
         else:
             self.add_message('assignment-from-none', node=node)
Example #22
0
    def visit_asyncwith(self, node):
        for ctx_mgr, _ in node.items:
            infered = checker_utils.safe_infer(ctx_mgr)
            if infered is None or infered is astroid.YES:
                continue

            if isinstance(infered, astroid.Instance):
                try:
                    infered.getattr('__aenter__')
                    infered.getattr('__aexit__')
                except exceptions.NotFoundError:
                    if isinstance(infered, astroid.Instance):
                        # If we do not know the bases of this class,
                        # just skip it.
                        if not checker_utils.has_known_bases(infered):
                            continue
                        # Just ignore mixin classes.
                        if self._ignore_mixin_members:
                            if infered.name[-5:].lower() == 'mixin':
                                continue
                else:
                    continue

            self.add_message('not-async-context-manager',
                             node=node, args=(infered.name, ))
Example #23
0
 def visit_callfunc(self, node):
     """check that called method are infered to callable objects
     """
     called = safe_infer(node.func)
     # only function, generator and object defining __call__ are allowed
     if called is not None and not called.callable():
         self.add_message('E1102', node=node, args=node.func.as_string())
Example #24
0
 def visit_callfunc(self, node):
     """Visit a CallFunc node."""
     if hasattr(node, 'func'):
         infer = utils.safe_infer(node.func)
         if infer and infer.root().name == '_io':
             if getattr(node.func, 'name', None) in ('open', 'file'):
                 self._check_open_encoding(node)
Example #25
0
 def visit_call(self, node):
     func = utils.safe_infer(node.func)
     if (isinstance(func, astroid.BoundMethod)
             and isinstance(func.bound, astroid.Instance)
             and func.bound.name in ('str', 'unicode', 'bytes')):
         if func.name == 'format':
             self._check_new_format(node, func)
Example #26
0
    def visit_slice(self, node):
        # Check the type of each part of the slice
        for index in (node.lower, node.upper, node.step):
            if index is None:
                continue

            index_type = safe_infer(index)

            if index_type is None or index_type is astroid.YES:
                continue

            # Constants must of type int or None
            if isinstance(index_type, astroid.Const):
                if isinstance(index_type.value, (int, type(None))):
                    continue
            # Instance values must be of type int, None or an object
            # with __index__
            elif isinstance(index_type, astroid.Instance):
                if index_type.pytype() in (BUILTINS + '.int',
                                           BUILTINS + '.NoneType'):
                    continue

                try:
                    index_type.getattr('__index__')
                    return
                except astroid.NotFoundError:
                    pass

            # Anything else is an error
            self.add_message('invalid-slice-index', node=node)
Example #27
0
 def visit_call(self, node):
     """Visit a Call node."""
     if hasattr(node, 'func'):
         infer = utils.safe_infer(node.func)
         if infer and infer.root().name == 'qutebrowser.config.config':
             if getattr(node.func, 'attrname', None) in ['get', 'set']:
                 self._check_config(node)
Example #28
0
    def visit_subscript(self, node):
        supported_protocol = None
        if isinstance(node.value, (astroid.ListComp, astroid.DictComp)):
            return

        if node.ctx == astroid.Load:
            supported_protocol = supports_getitem
            msg = "unsubscriptable-object"
        elif node.ctx == astroid.Store:
            supported_protocol = supports_setitem
            msg = "unsupported-assignment-operation"
        elif node.ctx == astroid.Del:
            supported_protocol = supports_delitem
            msg = "unsupported-delete-operation"

        if isinstance(node.value, astroid.SetComp):
            self.add_message(msg, args=node.value.as_string(), node=node.value)
            return

        if is_inside_abstract_class(node):
            return

        inferred = safe_infer(node.value)
        if inferred is None or inferred is astroid.YES:
            return

        if not supported_protocol(inferred):
            self.add_message(msg, args=node.value.as_string(), node=node.value)
Example #29
0
def _no_context_variadic(node, variadic_name, variadic_type, variadics):
    """Verify if the given call node has variadic nodes without context

    This is a workaround for handling cases of nested call functions
    which don't have the specific call context at hand.
    Variadic arguments (variable positional arguments and variable
    keyword arguments) are inferred, inherently wrong, by astroid
    as a Tuple, respectively a Dict with empty elements.
    This can lead pylint to believe that a function call receives
    too few arguments.
    """
    statement = node.statement()
    for name in statement.nodes_of_class(astroid.Name):
        if name.name != variadic_name:
            continue

        inferred = safe_infer(name)
        if isinstance(inferred, (astroid.List, astroid.Tuple)):
            length = len(inferred.elts)
        elif isinstance(inferred, astroid.Dict):
            length = len(inferred.items)
        else:
            continue

        inferred_statement = inferred.statement()
        if not length and isinstance(inferred_statement, astroid.FunctionDef):
            is_in_starred_context = _has_parent_of_type(node, variadic_type, statement)
            used_as_starred_argument = _is_name_used_as_variadic(name, variadics)
            if is_in_starred_context or used_as_starred_argument:
                return True
    return False
Example #30
0
    def _no_context_variadic(node):
        """Verify if the given call node has variadic nodes without context

        This is a workaround for handling cases of nested call functions
        which don't have the specific call context at hand.
        Variadic arguments (variable positional arguments and variable
        keyword arguments) are inferred, inherently wrong, by astroid
        as a Tuple, respectively a Dict with empty elements.
        This can lead pylint to believe that a function call receives
        too few arguments.
        """
        for arg in node.args:
            if not isinstance(arg, astroid.Starred):
                continue

            inferred = safe_infer(arg.value)
            if isinstance(inferred, astroid.Tuple):
                length = len(inferred.elts)
            elif isinstance(inferred, astroid.Dict):
                length = len(inferred.items)
            else:
                return False
            if not length and isinstance(inferred.statement(), astroid.FunctionDef):
                return True
        return False
Example #31
0
    def _check_in_slots(self, node):
        """ Check that the given assattr node
        is defined in the class slots.
        """
        infered = safe_infer(node.expr)
        if infered and isinstance(infered, Instance):
            klass = infered._proxied
            if '__slots__' not in klass.locals or not klass.newstyle:
                return

            slots = klass.slots()
            # If any ancestor doesn't use slots, the slots
            # defined for this class are superfluous.
            if any('__slots__' not in ancestor.locals and
                   ancestor.name != 'object'
                   for ancestor in klass.ancestors()):
                return

            if not any(slot.value == node.attrname for slot in slots):
                # If we have a '__dict__' in slots, then
                # assigning any name is valid.
                if not any(slot.value == '__dict__' for slot in slots):
                    self.add_message('assigning-non-slot',
                                     args=(node.attrname, ), node=node)
Example #32
0
    def visit_asyncwith(self, node: nodes.AsyncWith) -> None:
        for ctx_mgr, _ in node.items:
            inferred = checker_utils.safe_infer(ctx_mgr)
            if inferred is None or inferred is astroid.Uninferable:
                continue

            if isinstance(inferred, nodes.AsyncFunctionDef):
                # Check if we are dealing with a function decorated
                # with contextlib.asynccontextmanager.
                if decorated_with(inferred, self._async_generators):
                    continue
            elif isinstance(inferred, astroid.bases.AsyncGenerator):
                # Check if we are dealing with a function decorated
                # with contextlib.asynccontextmanager.
                if decorated_with(inferred.parent, self._async_generators):
                    continue
            else:
                try:
                    inferred.getattr("__aenter__")
                    inferred.getattr("__aexit__")
                except astroid.exceptions.NotFoundError:
                    if isinstance(inferred, astroid.Instance):
                        # If we do not know the bases of this class,
                        # just skip it.
                        if not checker_utils.has_known_bases(inferred):
                            continue
                        # Ignore mixin classes if they match the rgx option.
                        if self._ignore_mixin_members and self._mixin_class_rgx.match(
                            inferred.name
                        ):
                            continue
                else:
                    continue
            self.add_message(
                "not-async-context-manager", node=node, args=(inferred.name,)
            )
Example #33
0
    def _check_use_maxsplit_arg(self, node: astroid.Call) -> None:
        """Add message when accessing first or last elements of a str.split() or str.rsplit()."""

        # Check if call is split() or rsplit()
        if (
            isinstance(node.func, astroid.Attribute)
            and node.func.attrname in ("split", "rsplit")
            and isinstance(utils.safe_infer(node.func), astroid.BoundMethod)
        ):
            try:
                utils.get_argument_from_call(node, 0, "sep")
            except utils.NoSuchArgumentError:
                return

            try:
                # Ignore if maxsplit arg has been set
                utils.get_argument_from_call(node, 1, "maxsplit")
                return
            except utils.NoSuchArgumentError:
                pass

            if isinstance(node.parent, astroid.Subscript):
                try:
                    subscript_value = utils.get_subscript_const_value(node.parent).value
                except utils.InferredTypeError:
                    return

                if subscript_value in (-1, 0):
                    fn_name = node.func.attrname
                    new_fn = "rsplit" if subscript_value == -1 else "split"
                    new_name = (
                        node.func.as_string().rsplit(fn_name, maxsplit=1)[0]
                        + new_fn
                        + f"({node.args[0].as_string()}, maxsplit=1)[{subscript_value}]"
                    )
                    self.add_message("use-maxsplit-arg", node=node, args=(new_name,))
Example #34
0
def nodeisinstance(node, klasses, check_base_classes=True):
    if not isinstance(node, astng.Class):
        return False

    for base in node.bases:
        val = safe_infer(base)
        if not val:
            continue
        if isinstance(val, astng.bases._Yes):
            continue

        nodes = [val]
        if check_base_classes:
            try:
                nodes = chain([val], val.ancestors())
            except TypeError:
                pass

        for node in nodes:
            qual = '%s.%s' % (node.root().name, node.name)
            if qual in klasses:
                return True

    return False
Example #35
0
    def visit_tuple(self, tuple_node):
        if PY3K or not tuple_node.elts:
            self._checker.add_message('raising-bad-type',
                                      node=self._node,
                                      args='tuple')
            return

        # On Python 2, using the following is not an error:
        #    raise (ZeroDivisionError, None)
        #    raise (ZeroDivisionError, )
        # What's left to do is to check that the first
        # argument is indeed an exception. Verifying the other arguments
        # is not the scope of this check.
        first = tuple_node.elts[0]
        inferred = utils.safe_infer(first)
        if not inferred or inferred is astroid.Uninferable:
            return

        if (isinstance(inferred, astroid.Instance)
                and inferred.__class__.__name__ != 'Instance'):
            # TODO: explain why
            self.visit_default(tuple_node)
        else:
            self.visit(inferred)
Example #36
0
    def visit_call(self, node):
        # Check both for uses of os.putenv and os.setenv and modifying calls
        # to the os.environ object, such as os.environ.update

        if not isinstance(node, astroid.CallFunc):
            return

        function_node = safe_infer(node.func)
        if not isinstance(function_node, (astroid.Function, astroid.BoundMethod)):
            return

        # If the function is from the os or posix modules, look for calls that
        # modify the environment
        if function_node.root().name in ("os", os.name) and \
                function_node.name in ("putenv", "unsetenv"):
            self.add_message("environment-modify", node=node)

        # Look for methods bound to the environ dict
        if isinstance(function_node, astroid.BoundMethod) and \
                isinstance(function_node.bound, astroid.Dict) and \
                function_node.bound.root().name in ("os", os.name) and \
                function_node.bound.name == "environ" and \
                function_node.name in ("clear", "pop", "popitem", "setdefault", "update"):
            self.add_message("environment-modify", node=node)
Example #37
0
 def _is_builtin(node, function):
     inferred = utils.safe_infer(node)
     if not inferred:
         return False
     return utils.is_builtin_object(inferred) and inferred.name == function
Example #38
0
    def _check_dict_consider_namedtuple_dataclass(self,
                                                  node: nodes.Dict) -> None:
        """Check if dictionary values can be replaced by Namedtuple or Dataclass."""
        if not (isinstance(node.parent, (nodes.Assign, nodes.AnnAssign))
                and isinstance(node.parent.parent, nodes.Module)
                or isinstance(node.parent, nodes.AnnAssign)
                and isinstance(node.parent.target, nodes.AssignName)
                and utils.is_assign_name_annotated_with(
                    node.parent.target, "Final")):
            # If dict is not part of an 'Assign' or 'AnnAssign' node in
            # a module context OR 'AnnAssign' with 'Final' annotation, skip check.
            return

        # All dict_values are itself dict nodes
        if len(node.items) > 1 and all(
                isinstance(dict_value, nodes.Dict)
                for _, dict_value in node.items):
            KeyTupleT = Tuple[Type[nodes.NodeNG], str]

            # Makes sure all keys are 'Const' string nodes
            keys_checked: Set[KeyTupleT] = set()
            for _, dict_value in node.items:
                dict_value = cast(nodes.Dict, dict_value)
                for key, _ in dict_value.items:
                    key_tuple = (type(key), key.as_string())
                    if key_tuple in keys_checked:
                        continue
                    inferred = safe_infer(key)
                    if not (isinstance(inferred, nodes.Const)
                            and inferred.pytype() == "builtins.str"):
                        return
                    keys_checked.add(key_tuple)

            # Makes sure all subdicts have at least 1 common key
            key_tuples: List[Tuple[KeyTupleT, ...]] = []
            for _, dict_value in node.items:
                dict_value = cast(nodes.Dict, dict_value)
                key_tuples.append(
                    tuple((type(key), key.as_string())
                          for key, _ in dict_value.items))
            keys_intersection: Set[KeyTupleT] = set(key_tuples[0])
            for sub_key_tuples in key_tuples[1:]:
                keys_intersection.intersection_update(sub_key_tuples)
            if not keys_intersection:
                return

            self.add_message("consider-using-namedtuple-or-dataclass",
                             node=node)
            return

        # All dict_values are itself either list or tuple nodes
        if len(node.items) > 1 and all(
                isinstance(dict_value, (nodes.List, nodes.Tuple))
                for _, dict_value in node.items):
            # Make sure all sublists have the same length > 0
            list_length = len(node.items[0][1].elts)
            if list_length == 0:
                return
            for _, dict_value in node.items[1:]:
                dict_value = cast(Union[nodes.List, nodes.Tuple], dict_value)
                if len(dict_value.elts) != list_length:
                    return

            # Make sure at least one list entry isn't a dict
            for _, dict_value in node.items:
                dict_value = cast(Union[nodes.List, nodes.Tuple], dict_value)
                if all(
                        isinstance(entry, nodes.Dict)
                        for entry in dict_value.elts):
                    return

            self.add_message("consider-using-namedtuple-or-dataclass",
                             node=node)
            return
Example #39
0
    def _is_node_return_ended(self, node):
        """Check if the node ends with an explicit return statement.

        Args:
            node (astroid.NodeNG): node to be checked.

        Returns:
            bool: True if the node ends with an explicit statement, False otherwise.

        """
        # Recursion base case
        if isinstance(node, astroid.Return):
            return True
        if isinstance(node, astroid.Call):
            try:
                funcdef_node = node.func.infered()[0]
                if self._is_function_def_never_returning(funcdef_node):
                    return True
            except astroid.InferenceError:
                pass
        # Avoid the check inside while loop as we don't know
        # if they will be completed
        if isinstance(node, astroid.While):
            return True
        if isinstance(node, astroid.Raise):
            # a Raise statement doesn't need to end with a return statement
            # but if the exception raised is handled, then the handler has to
            # ends with a return statement
            if not node.exc:
                # Ignore bare raises
                return True
            if not utils.is_node_inside_try_except(node):
                # If the raise statement is not inside a try/except statement
                # then the exception is raised and cannot be caught. No need
                # to infer it.
                return True
            exc = utils.safe_infer(node.exc)
            if exc is None or exc is astroid.Uninferable:
                return False
            exc_name = exc.pytype().split('.')[-1]
            handlers = utils.get_exception_handlers(node, exc_name)
            handlers = list(handlers) if handlers is not None else []
            if handlers:
                # among all the handlers handling the exception at least one
                # must end with a return statement
                return any(
                    self._is_node_return_ended(_handler)
                    for _handler in handlers)
            # if no handlers handle the exception then it's ok
            return True
        if isinstance(node, astroid.If):
            # if statement is returning if there are exactly two return statements in its
            # children : one for the body part, the other for the orelse part
            # Do not check if inner function definition are return ended.
            return_stmts = [
                self._is_node_return_ended(_child)
                for _child in node.get_children()
                if not isinstance(_child, astroid.FunctionDef)
            ]
            return sum(return_stmts) == 2
        # recurses on the children of the node except for those which are except handler
        # because one cannot be sure that the handler will really be used
        return any(
            self._is_node_return_ended(_child)
            for _child in node.get_children()
            if not isinstance(_child, astroid.ExceptHandler))
Example #40
0
    def _check_use_implicit_booleaness_not_comparison(
        self, node: nodes.Compare
    ) -> None:
        """Check for left side and right side of the node for empty literals"""
        is_left_empty_literal = utils.is_base_container(
            node.left
        ) or utils.is_empty_dict_literal(node.left)

        # Check both left-hand side and right-hand side for literals
        for operator, comparator in node.ops:
            is_right_empty_literal = utils.is_base_container(
                comparator
            ) or utils.is_empty_dict_literal(comparator)
            # Using Exclusive OR (XOR) to compare between two side.
            # If two sides are both literal, it should be different error.
            if is_right_empty_literal ^ is_left_empty_literal:
                # set target_node to opposite side of literal
                target_node = node.left if is_right_empty_literal else comparator
                literal_node = comparator if is_right_empty_literal else node.left
                # Infer node to check
                target_instance = utils.safe_infer(target_node)
                if target_instance is None:
                    continue
                mother_classes = self.base_classes_of_node(target_instance)
                is_base_comprehension_type = any(
                    t in mother_classes for t in ("tuple", "list", "dict", "set")
                )

                # Only time we bypass check is when target_node is not inherited by
                # collection literals and have its own __bool__ implementation.
                if not is_base_comprehension_type and self.instance_has_bool(
                    target_instance
                ):
                    continue

                # No need to check for operator when visiting compare node
                if operator in {"==", "!=", ">=", ">", "<=", "<"}:
                    collection_literal = "{}"
                    if isinstance(literal_node, nodes.List):
                        collection_literal = "[]"
                    if isinstance(literal_node, nodes.Tuple):
                        collection_literal = "()"

                    instance_name = "x"
                    if isinstance(target_node, nodes.Call) and target_node.func:
                        instance_name = f"{target_node.func.as_string()}(...)"
                    elif isinstance(target_node, (nodes.Attribute, nodes.Name)):
                        instance_name = target_node.as_string()

                    original_comparison = (
                        f"{instance_name} {operator} {collection_literal}"
                    )
                    suggestion = (
                        f"{instance_name}"
                        if operator == "!="
                        else f"not {instance_name}"
                    )
                    self.add_message(
                        "use-implicit-booleaness-not-comparison",
                        args=(
                            original_comparison,
                            suggestion,
                        ),
                        node=node,
                    )
Example #41
0
    def visit_call(self, node):
        """Visits each function call in a lint check.

        Args:
            node: Call. The current function call node.
        """
        called = checker_utils.safe_infer(node.func)

        try:
            # For the rationale behind the Pylint pragma below,
            # see https://stackoverflow.com/a/35701863/8115428
            called, implicit_args, callable_name = (
                typecheck._determine_callable(called))  # pylint: disable=protected-access
        except ValueError:
            return

        if called.args.args is None:
            # Built-in functions have no argument information.
            return

        if len(called.argnames()) != len(set(called.argnames())):
            return

        # Build the set of keyword arguments and count the positional arguments.
        call_site = astroid.arguments.CallSite.from_call(node)
        if call_site.has_invalid_arguments() or (
                call_site.has_invalid_keywords()):
            return

        num_positional_args = len(call_site.positional_arguments)
        keyword_args = list(call_site.keyword_arguments.keys())

        already_filled_positionals = getattr(called, 'filled_positionals', 0)
        already_filled_keywords = getattr(called, 'filled_keywords', {})

        keyword_args += list(already_filled_keywords)
        num_positional_args += already_filled_positionals
        num_positional_args += implicit_args

        # Analyze the list of formal parameters.
        num_mandatory_parameters = len(called.args.args) - len(
            called.args.defaults)

        parameters = []
        parameter_name_to_index = {}
        for i, arg in enumerate(called.args.args):
            if isinstance(arg, astroid.Tuple):
                name = None
            else:
                assert isinstance(arg, astroid.AssignName)
                name = arg.name
                parameter_name_to_index[name] = i
            if i >= num_mandatory_parameters:
                defval = called.args.defaults[i - num_mandatory_parameters]
            else:
                defval = None
            parameters.append([(name, defval), False])

        num_positional_args_unused = num_positional_args
        # Check that all parameters with a default value have
        # been called explicitly.
        for [(name, defval), _] in parameters:
            if defval:
                if name is None:
                    display_name = '<tuple>'
                else:
                    display_name = repr(name)

                if name not in keyword_args and (
                        num_positional_args_unused >
                    (num_mandatory_parameters)) and (callable_name !=
                                                     'constructor'):
                    # This try/except block tries to get the function
                    # name. Since each node may differ, multiple
                    # blocks have been used.
                    try:
                        func_name = node.func.attrname
                    except AttributeError:
                        try:
                            func_name = node.func.name
                        except AttributeError:
                            func_name = node.func

                    self.add_message('non-explicit-keyword-args',
                                     node=node,
                                     args=(display_name, callable_name,
                                           func_name))
                    num_positional_args_unused -= 1
Example #42
0
    def visit_binop(self, node):
        if node.op != "%":
            return
        left = node.left
        args = node.right

        if not (isinstance(left, astroid.Const) and isinstance(left.value, str)):
            return
        format_string = left.value
        try:
            (
                required_keys,
                required_num_args,
                required_key_types,
                required_arg_types,
            ) = utils.parse_format_string(format_string)
        except utils.UnsupportedFormatCharacter as exc:
            formatted = format_string[exc.index]
            self.add_message(
                "bad-format-character",
                node=node,
                args=(formatted, ord(formatted), exc.index),
            )
            return
        except utils.IncompleteFormatString:
            self.add_message("truncated-format-string", node=node)
            return
        if required_keys and required_num_args:
            # The format string uses both named and unnamed format
            # specifiers.
            self.add_message("mixed-format-string", node=node)
        elif required_keys:
            # The format string uses only named format specifiers.
            # Check that the RHS of the % operator is a mapping object
            # that contains precisely the set of keys required by the
            # format string.
            if isinstance(args, astroid.Dict):
                keys = set()
                unknown_keys = False
                for k, _ in args.items:
                    if isinstance(k, astroid.Const):
                        key = k.value
                        if isinstance(key, str):
                            keys.add(key)
                        else:
                            self.add_message(
                                "bad-format-string-key", node=node, args=key
                            )
                    else:
                        # One of the keys was something other than a
                        # constant.  Since we can't tell what it is,
                        # suppress checks for missing keys in the
                        # dictionary.
                        unknown_keys = True
                if not unknown_keys:
                    for key in required_keys:
                        if key not in keys:
                            self.add_message(
                                "missing-format-string-key", node=node, args=key
                            )
                for key in keys:
                    if key not in required_keys:
                        self.add_message(
                            "unused-format-string-key", node=node, args=key
                        )
                for key, arg in args.items:
                    if not isinstance(key, astroid.Const):
                        continue
                    format_type = required_key_types.get(key.value, None)
                    arg_type = utils.safe_infer(arg)
                    if (
                        format_type is not None
                        and arg_type not in (None, astroid.Uninferable)
                        and not arg_matches_format_type(arg_type, format_type)
                    ):
                        self.add_message(
                            "bad-string-format-type",
                            node=node,
                            args=(arg_type.pytype(), format_type),
                        )
            elif isinstance(args, (OTHER_NODES, astroid.Tuple)):
                type_name = type(args).__name__
                self.add_message("format-needs-mapping", node=node, args=type_name)
            # else:
            # The RHS of the format specifier is a name or
            # expression.  It may be a mapping object, so
            # there's nothing we can check.
        else:
            # The format string uses only unnamed format specifiers.
            # Check that the number of arguments passed to the RHS of
            # the % operator matches the number required by the format
            # string.
            args_elts = ()
            if isinstance(args, astroid.Tuple):
                rhs_tuple = utils.safe_infer(args)
                num_args = None
                if hasattr(rhs_tuple, "elts"):
                    args_elts = rhs_tuple.elts
                    num_args = len(args_elts)
            elif isinstance(args, (OTHER_NODES, (astroid.Dict, astroid.DictComp))):
                args_elts = [args]
                num_args = 1
            else:
                # The RHS of the format specifier is a name or
                # expression.  It could be a tuple of unknown size, so
                # there's nothing we can check.
                num_args = None
            if num_args is not None:
                if num_args > required_num_args:
                    self.add_message("too-many-format-args", node=node)
                elif num_args < required_num_args:
                    self.add_message("too-few-format-args", node=node)
                for arg, format_type in zip(args_elts, required_arg_types):
                    if not arg:
                        continue
                    arg_type = utils.safe_infer(arg)
                    if (
                        arg_type
                        not in (
                            None,
                            astroid.Uninferable,
                        )
                        and not arg_matches_format_type(arg_type, format_type)
                    ):
                        self.add_message(
                            "bad-string-format-type",
                            node=node,
                            args=(arg_type.pytype(), format_type),
                        )
Example #43
0
    def visit_callfunc(self, node):
        if not is_model(node.frame()):
            # We only care about fields attached to models
            return

        val = safe_infer(node)
        if not val or not val.root().name.startswith(
                'django.db.models.fields'):
            # Not a field
            return

        assname = '(unknown name)'
        x = node.parent.get_children().next()
        if isinstance(x, astng.AssName):
            assname = x.name

        self.field_count += 1

        # Parse kwargs
        options = dict([(option, None) for option in (
            'null',
            'blank',
            'unique',
            'default',
            'auto_now',
            'primary_key',
            'auto_now_add',
            'verify_exists',
            'related_name',
            'max_length',
            'unique_for_date',
            'unique_for_month',
            'unique_for_year',
        )])

        for arg in node.args:
            if not isinstance(arg, astng.Keyword):
                continue

            for option in options.keys():
                if arg.arg == option:
                    try:
                        options[option] = safe_infer(arg.value).value
                    except AttributeError:
                        # Don't lint this field if we cannot infer everything
                        return

        if not val.name.lower().startswith('null'):
            for option in ('null', 'blank'):
                if options[option] is False:
                    self.add_message('W6015',
                                     node=node,
                                     args=(
                                         assname,
                                         option,
                                     ))

        # Field type specific checks
        if val.name in ('CharField', 'TextField'):
            if options['null']:
                self.add_message('W6000', node=node, args=(assname, ))

            if val.name == 'CharField' and \
                    options['max_length'] > self.config.max_charfield_length:
                self.add_message('W6007',
                                 node=node,
                                 args=(
                                     assname,
                                     options['max_length'],
                                     self.config.max_charfield_length,
                                 ))

        elif val.name == 'BooleanField':
            if options['default']:
                self.add_message('W6012', node=node, args=(assname, ))

        elif val.name == 'ForeignKey':
            val = safe_infer(node.args[0])
            if isinstance(val, astng.Const) and val.value == 'self':
                self.add_message('W6001', node=node, args=(assname, ))

            elif not options['related_name']:
                self.add_message('W6006', node=node, args=(assname, ))

            if options['primary_key'] and options['unique'] is False:
                self.add_message('W6014', node=node, args=(assname, ))
            elif options['primary_key'] or options['unique']:
                self.add_message('W6013', node=node, args=(assname, ))

        elif val.name == 'URLField':
            if options['verify_exists'] is None:
                self.add_message('W6011', node=node, args=(assname, ))

        elif val.name in ('PositiveSmallIntegerField', 'SmallIntegerField'):
            self.add_message('W6010', node=node, args=(assname, val.name))

        elif val.name == 'NullBooleanField':
            self.add_message('W6009', node=node, args=(assname, ))

        elif val.name == 'ManyToManyField':
            if options['null']:
                self.add_message('W6016', node=node, args=(assname, ))

        # Generic checks
        if options['null'] and not options['blank']:
            self.add_message('W6004', node=node, args=(assname, ))

        if options['auto_now'] or options['auto_now_add']:
            self.add_message('W6008', node=node, args=(assname, ))

        for suffix in ('date', 'month', 'year'):
            if options['unique_for_%s' % suffix]:
                self.add_message('W6005', node=node, args=(assname, suffix))
Example #44
0
    def _check_using_constant_test(
        self,
        node: nodes.If | nodes.IfExp | nodes.Comprehension,
        test: nodes.NodeNG | None,
    ) -> None:
        const_nodes = (
            nodes.Module,
            nodes.GeneratorExp,
            nodes.Lambda,
            nodes.FunctionDef,
            nodes.ClassDef,
            astroid.bases.Generator,
            astroid.UnboundMethod,
            astroid.BoundMethod,
            nodes.Module,
        )
        structs = (nodes.Dict, nodes.Tuple, nodes.Set, nodes.List)

        # These nodes are excepted, since they are not constant
        # values, requiring a computation to happen.
        except_nodes = (
            nodes.Call,
            nodes.BinOp,
            nodes.BoolOp,
            nodes.UnaryOp,
            nodes.Subscript,
        )
        inferred = None
        emit = isinstance(test, (nodes.Const, ) + structs + const_nodes)
        maybe_generator_call = None
        if not isinstance(test, except_nodes):
            inferred = utils.safe_infer(test)
            if inferred is astroid.Uninferable and isinstance(
                    test, nodes.Name):
                emit, maybe_generator_call = BasicChecker._name_holds_generator(
                    test)

        # Emit if calling a function that only returns GeneratorExp (always tests True)
        elif isinstance(test, nodes.Call):
            maybe_generator_call = test
        if maybe_generator_call:
            inferred_call = utils.safe_infer(maybe_generator_call.func)
            if isinstance(inferred_call, nodes.FunctionDef):
                # Can't use all(x) or not any(not x) for this condition, because it
                # will return True for empty generators, which is not what we want.
                all_returns_were_generator = None
                for return_node in inferred_call._get_return_nodes_skip_functions(
                ):
                    if not isinstance(return_node.value, nodes.GeneratorExp):
                        all_returns_were_generator = False
                        break
                    all_returns_were_generator = True
                if all_returns_were_generator:
                    self.add_message("using-constant-test",
                                     node=node,
                                     confidence=INFERENCE)
                    return

        if emit:
            self.add_message("using-constant-test",
                             node=test,
                             confidence=INFERENCE)
        elif isinstance(inferred, const_nodes):
            # If the constant node is a FunctionDef or Lambda then
            # it may be an illicit function call due to missing parentheses
            call_inferred = None
            try:
                if isinstance(inferred, nodes.FunctionDef):
                    call_inferred = inferred.infer_call_result()
                elif isinstance(inferred, nodes.Lambda):
                    call_inferred = inferred.infer_call_result(node)
            except astroid.InferenceError:
                call_inferred = None
            if call_inferred:
                try:
                    for inf_call in call_inferred:
                        if inf_call != astroid.Uninferable:
                            self.add_message(
                                "missing-parentheses-for-call-in-test",
                                node=test,
                                confidence=INFERENCE,
                            )
                            break
                except astroid.InferenceError:
                    pass
            self.add_message("using-constant-test",
                             node=test,
                             confidence=INFERENCE)
Example #45
0
    def _check_try_except_raise(self, node):
        def gather_exceptions_from_handler(
            handler,
        ) -> typing.Optional[typing.List[astroid.node_classes.NodeNG]]:
            exceptions = []  # type: typing.List[astroid.node_classes.NodeNG]
            if handler.type:
                exceptions_in_handler = utils.safe_infer(handler.type)
                if isinstance(exceptions_in_handler, astroid.Tuple):
                    exceptions = list({
                        exception
                        for exception in exceptions_in_handler.elts
                        if isinstance(exception, astroid.Name)
                    })
                elif exceptions_in_handler:
                    exceptions = [exceptions_in_handler]
                else:
                    # Break when we cannot infer anything reliably.
                    return None
            return exceptions

        bare_raise = False
        handler_having_bare_raise = None
        excs_in_bare_handler = []
        for handler in node.handlers:
            if bare_raise:
                # check that subsequent handler is not parent of handler which had bare raise.
                # since utils.safe_infer can fail for bare except, check it before.
                # also break early if bare except is followed by bare except.

                excs_in_current_handler = gather_exceptions_from_handler(
                    handler)

                if not excs_in_current_handler:
                    bare_raise = False
                    break
                if excs_in_bare_handler is None:
                    # It can be `None` when the inference failed
                    break

                for exc_in_current_handler in excs_in_current_handler:
                    inferred_current = utils.safe_infer(exc_in_current_handler)
                    if any(
                            utils.is_subclass_of(
                                utils.safe_infer(exc_in_bare_handler),
                                inferred_current)
                            for exc_in_bare_handler in excs_in_bare_handler):
                        bare_raise = False
                        break

            # `raise` as the first operator inside the except handler
            if _is_raising([handler.body[0]]):
                # flags when there is a bare raise
                if handler.body[0].exc is None:
                    bare_raise = True
                    handler_having_bare_raise = handler
                    excs_in_bare_handler = gather_exceptions_from_handler(
                        handler)
        else:
            if bare_raise:
                self.add_message("try-except-raise",
                                 node=handler_having_bare_raise)
Example #46
0
    def visit_callfunc(self, node):
        """check that called functions/methods are inferred to callable objects,
        and that the arguments passed to the function match the parameters in
        the inferred function's definition
        """
        # Build the set of keyword arguments, checking for duplicate keywords,
        # and count the positional arguments.
        keyword_args = set()
        num_positional_args = 0
        for arg in node.args:
            if isinstance(arg, astroid.Keyword):
                keyword = arg.arg
                if keyword in keyword_args:
                    self.add_message('duplicate-keyword-arg', node=node, args=keyword)
                keyword_args.add(keyword)
            else:
                num_positional_args += 1

        called = safe_infer(node.func)
        # only function, generator and object defining __call__ are allowed
        if called is not None and not called.callable():
            self.add_message('not-callable', node=node, args=node.func.as_string())

        try:
            called, implicit_args, callable_name = _determine_callable(called)
        except ValueError:
            # Any error occurred during determining the function type, most of 
            # those errors are handled by different warnings.
            return
        num_positional_args += implicit_args
        if called.args.args is None:
            # Built-in functions have no argument information.
            return

        if len(called.argnames()) != len(set(called.argnames())):
            # Duplicate parameter name (see E9801).  We can't really make sense
            # of the function call in this case, so just return.
            return

        # Analyze the list of formal parameters.
        num_mandatory_parameters = len(called.args.args) - len(called.args.defaults)
        parameters = []
        parameter_name_to_index = {}
        for i, arg in enumerate(called.args.args):
            if isinstance(arg, astroid.Tuple):
                name = None
                # Don't store any parameter names within the tuple, since those
                # are not assignable from keyword arguments.
            else:
                if isinstance(arg, astroid.Keyword):
                    name = arg.arg
                else:
                    assert isinstance(arg, astroid.AssName)
                    # This occurs with:
                    #    def f( (a), (b) ): pass
                    name = arg.name
                parameter_name_to_index[name] = i
            if i >= num_mandatory_parameters:
                defval = called.args.defaults[i - num_mandatory_parameters]
            else:
                defval = None
            parameters.append([(name, defval), False])

        kwparams = {}
        for i, arg in enumerate(called.args.kwonlyargs):
            if isinstance(arg, astroid.Keyword):
                name = arg.arg
            else:
                assert isinstance(arg, astroid.AssName)
                name = arg.name
            kwparams[name] = [called.args.kw_defaults[i], False]

        # Match the supplied arguments against the function parameters.

        # 1. Match the positional arguments.
        for i in range(num_positional_args):
            if i < len(parameters):
                parameters[i][1] = True
            elif called.args.vararg is not None:
                # The remaining positional arguments get assigned to the *args
                # parameter.
                break
            else:
                # Too many positional arguments.
                self.add_message('too-many-function-args', node=node, args=(callable_name,))
                break

        # 2. Match the keyword arguments.
        for keyword in keyword_args:
            if keyword in parameter_name_to_index:
                i = parameter_name_to_index[keyword]
                if parameters[i][1]:
                    # Duplicate definition of function parameter.
                    self.add_message('redundant-keyword-arg', node=node, args=(keyword, callable_name))
                else:
                    parameters[i][1] = True
            elif keyword in kwparams:
                if kwparams[keyword][1]:  # XXX is that even possible?
                    # Duplicate definition of function parameter.
                    self.add_message('redundant-keyword-arg', node=node, args=(keyword, callable_name))
                else:
                    kwparams[keyword][1] = True
            elif called.args.kwarg is not None:
                # The keyword argument gets assigned to the **kwargs parameter.
                pass
            else:
                # Unexpected keyword argument.
                self.add_message('unexpected-keyword-arg', node=node, args=(keyword, callable_name))

        # 3. Match the *args, if any.  Note that Python actually processes
        #    *args _before_ any keyword arguments, but we wait until after
        #    looking at the keyword arguments so as to make a more conservative
        #    guess at how many values are in the *args sequence.
        if node.starargs is not None:
            for i in range(num_positional_args, len(parameters)):
                [(name, defval), assigned] = parameters[i]
                # Assume that *args provides just enough values for all
                # non-default parameters after the last parameter assigned by
                # the positional arguments but before the first parameter
                # assigned by the keyword arguments.  This is the best we can
                # get without generating any false positives.
                if (defval is not None) or assigned:
                    break
                parameters[i][1] = True

        # 4. Match the **kwargs, if any.
        if node.kwargs is not None:
            for i, [(name, defval), assigned] in enumerate(parameters):
                # Assume that *kwargs provides values for all remaining
                # unassigned named parameters.
                if name is not None:
                    parameters[i][1] = True
                else:
                    # **kwargs can't assign to tuples.
                    pass

        # Check that any parameters without a default have been assigned
        # values.
        for [(name, defval), assigned] in parameters:
            if (defval is None) and not assigned:
                if name is None:
                    display_name = '<tuple>'
                else:
                    display_name = repr(name)
                self.add_message('no-value-for-parameter', node=node, args=(display_name, callable_name))

        for name in kwparams:
            defval, assigned = kwparams[name]
            if defval is None and not assigned:
                self.add_message('missing-kwoa', node=node, args=(name, callable_name))
Example #47
0
def _is_call2custom_manager(node):
    """Checks if the call is being done to a custom queryset manager."""
    called = safe_infer(node.func)
    funcdef = getattr(called, '_proxied', None)
    return _is_custom_qs_manager(funcdef)
Example #48
0
 def _check_raise_value(self, node, expr):
     """check for bad values, string exception and class inheritance
     """
     value_found = True
     if isinstance(expr, astroid.Const):
         value = expr.value
         if not isinstance(value, str):
             # raising-string will be emitted from python3 porting checker.
             self.add_message('raising-bad-type',
                              node=node,
                              args=value.__class__.__name__)
     elif ((isinstance(expr, astroid.Name)
            and expr.name in ('None', 'True', 'False'))
           or isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple,
                                astroid.Module, astroid.Function))):
         emit = True
         if not PY3K and isinstance(expr, astroid.Tuple):
             # On Python 2, using the following is not an error:
             #    raise (ZeroDivisionError, None)
             #    raise (ZeroDivisionError, )
             # What's left to do is to check that the first
             # argument is indeed an exception.
             # Verifying the other arguments is not
             # the scope of this check.
             first = expr.elts[0]
             inferred = safe_infer(first)
             if isinstance(inferred, Instance):
                 # pylint: disable=protected-access
                 inferred = inferred._proxied
             if (inferred is YES or isinstance(inferred, astroid.Class)
                     and inherit_from_std_ex(inferred)):
                 emit = False
         if emit:
             self.add_message('raising-bad-type', node=node, args=expr.name)
     elif (
         (isinstance(expr, astroid.Name) and expr.name == 'NotImplemented')
             or (isinstance(expr, astroid.CallFunc)
                 and isinstance(expr.func, astroid.Name)
                 and expr.func.name == 'NotImplemented')):
         self.add_message('notimplemented-raised', node=node)
     elif isinstance(expr, (Instance, astroid.Class)):
         if isinstance(expr, Instance):
             # pylint: disable=protected-access
             expr = expr._proxied
         if (isinstance(expr, astroid.Class)
                 and not inherit_from_std_ex(expr)):
             if expr.newstyle:
                 self.add_message('raising-non-exception', node=node)
             else:
                 if has_known_bases(expr):
                     confidence = INFERENCE
                 else:
                     confidence = INFERENCE_FAILURE
                 self.add_message('nonstandard-exception',
                                  node=node,
                                  confidence=confidence)
         else:
             value_found = False
     else:
         value_found = False
     return value_found
Example #49
0
    def visit_callfunc(self, node):
        """check that called functions/methods are inferred to callable objects,
        and that the arguments passed to the function match the parameters in
        the inferred function's definition
        """

        # Build the set of keyword arguments, checking for duplicate keywords,
        # and count the positional arguments.
        keyword_args = set()
        num_positional_args = 0
        for arg in node.args:
            if isinstance(arg, astng.Keyword):
                keyword = arg.arg
                if keyword in keyword_args:
                    self.add_message('E1122', node=node, args=keyword)
                keyword_args.add(keyword)
            else:
                num_positional_args += 1

        called = safe_infer(node.func)
        # only function, generator and object defining __call__ are allowed
        if called is not None and not called.callable():
            self.add_message('E1102', node=node, args=node.func.as_string())

        # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must
        # come first in this 'if..else'.
        if isinstance(called, astng.BoundMethod):
            # Bound methods have an extra implicit 'self' argument.
            num_positional_args += 1
        elif isinstance(called, astng.UnboundMethod):
            if called.decorators is not None:
                for d in called.decorators.nodes:
                    if isinstance(d, astng.Name) and (d.name == 'classmethod'):
                        # Class methods have an extra implicit 'cls' argument.
                        num_positional_args += 1
                        break
        elif (isinstance(called, astng.Function)
              or isinstance(called, astng.Lambda)):
            pass
        else:
            return

        if called.args.args is None:
            # Built-in functions have no argument information.
            return

        if len(called.argnames()) != len(set(called.argnames())):
            # Duplicate parameter name (see E9801).  We can't really make sense
            # of the function call in this case, so just return.
            return

        # Analyze the list of formal parameters.
        num_mandatory_parameters = len(called.args.args) - len(
            called.args.defaults)
        parameters = []
        parameter_name_to_index = {}
        for i, arg in enumerate(called.args.args):
            if isinstance(arg, astng.Tuple):
                name = None
                # Don't store any parameter names within the tuple, since those
                # are not assignable from keyword arguments.
            else:
                if isinstance(arg, astng.Keyword):
                    name = arg.arg
                else:
                    assert isinstance(arg, astng.AssName)
                    # This occurs with:
                    #    def f( (a), (b) ): pass
                    name = arg.name
                parameter_name_to_index[name] = i
            if i >= num_mandatory_parameters:
                defval = called.args.defaults[i - num_mandatory_parameters]
            else:
                defval = None
            parameters.append([(name, defval), False])

        # Match the supplied arguments against the function parameters.

        # 1. Match the positional arguments.
        for i in range(num_positional_args):
            if i < len(parameters):
                parameters[i][1] = True
            elif called.args.vararg is not None:
                # The remaining positional arguments get assigned to the *args
                # parameter.
                break
            else:
                # Too many positional arguments.
                self.add_message('E1121', node=node)
                break

        # 2. Match the keyword arguments.
        for keyword in keyword_args:
            if keyword in parameter_name_to_index:
                i = parameter_name_to_index[keyword]
                if parameters[i][1]:
                    # Duplicate definition of function parameter.
                    self.add_message('E1124', node=node, args=keyword)
                else:
                    parameters[i][1] = True
            elif called.args.kwarg is not None:
                # The keyword argument gets assigned to the **kwargs parameter.
                pass
            else:
                # Unexpected keyword argument.
                self.add_message('E1123', node=node, args=keyword)

        # 3. Match the *args, if any.  Note that Python actually processes
        #    *args _before_ any keyword arguments, but we wait until after
        #    looking at the keyword arguments so as to make a more conservative
        #    guess at how many values are in the *args sequence.
        if node.starargs is not None:
            for i in range(num_positional_args, len(parameters)):
                [(name, defval), assigned] = parameters[i]
                # Assume that *args provides just enough values for all
                # non-default parameters after the last parameter assigned by
                # the positional arguments but before the first parameter
                # assigned by the keyword arguments.  This is the best we can
                # get without generating any false positives.
                if (defval is not None) or assigned:
                    break
                parameters[i][1] = True

        # 4. Match the **kwargs, if any.
        if node.kwargs is not None:
            for i, [(name, defval), assigned] in enumerate(parameters):
                # Assume that *kwargs provides values for all remaining
                # unassigned named parameters.
                if name is not None:
                    parameters[i][1] = True
                else:
                    # **kwargs can't assign to tuples.
                    pass

        # Check that any parameters without a default have been assigned
        # values.
        for [(name, defval), assigned] in parameters:
            if (defval is None) and not assigned:
                if name is None:
                    display = '<tuple>'
                else:
                    display_name = repr(name)
                self.add_message('E1120', node=node, args=display_name)
Example #50
0
    def visit_call(self, node):
        infer_node = utils.safe_infer(node.func)
        if utils.is_builtin_object(infer_node) and infer_node.name == 'print':
            self.add_message('print-used', node=node)
        if ('fields' == self.get_func_lib(node.func)
                and isinstance(node.parent, astroid.Assign)
                and isinstance(node.parent.parent, astroid.ClassDef)):
            args = misc.join_node_args_kwargs(node)
            index = 0
            field_name = ''
            if (isinstance(node.parent, astroid.Assign)
                    and node.parent.targets and isinstance(
                        node.parent.targets[0], astroid.AssignName)):
                field_name = (node.parent.targets[0].name.replace('_', ' '))
            is_related = bool(
                [1 for kw in node.keywords or [] if kw.arg == 'related'])
            for argument in args:
                argument_aux = argument
                # Check this 'name = fields.Char("name")'
                if (not is_related and isinstance(argument, astroid.Const)
                        and (index == FIELDS_METHOD.get(
                            argument.parent.func.attrname, 0))
                        and (argument.value
                             in [field_name.capitalize(),
                                 field_name.title()])):
                    self.add_message('attribute-string-redundant', node=node)
                if isinstance(argument, astroid.Keyword):
                    argument_aux = argument.value
                    deprecated = self.config.deprecated_field_parameters
                    if argument.arg in ['compute', 'search', 'inverse'] and \
                            isinstance(argument_aux, astroid.Const) and \
                            isinstance(argument_aux.value, string_types) and \
                            not argument_aux.value.startswith(
                                '_' + argument.arg + '_'):
                        self.add_message('method-' + argument.arg,
                                         node=argument_aux)
                    # Check if the param string is equal to the name
                    #   of variable
                    elif not is_related and argument.arg == 'string' and \
                        (isinstance(argument_aux, astroid.Const) and
                         argument_aux.value in
                         [field_name.capitalize(), field_name.title()]):
                        self.add_message('attribute-string-redundant',
                                         node=node)
                    elif (argument.arg in deprecated):
                        self.add_message('renamed-field-parameter',
                                         node=node,
                                         args=(argument.arg,
                                               deprecated[argument.arg]))
                if isinstance(argument_aux, astroid.Call) and \
                        isinstance(argument_aux.func, astroid.Name) and \
                        argument_aux.func.name == '_':
                    self.add_message('translation-field', node=argument_aux)
                index += 1
        # Check cr.commit()
        if isinstance(node, astroid.Call) and \
                isinstance(node.func, astroid.Attribute) and \
                node.func.attrname == 'commit' and \
                self.get_cursor_name(node.func) in self.config.cursor_expr:
            self.add_message('invalid-commit', node=node)

        if (isinstance(node, astroid.Call)
                and isinstance(node.func, astroid.Attribute)
                and node.func.attrname == 'with_context' and not node.keywords
                and node.args):
            # with_context(**ctx) is considered a keywords
            # So, if only one args is received it is overridden
            self.add_message('context-overridden',
                             node=node,
                             args=(node.args[0].as_string(), ))

        # Call the message_post()
        base_dirname = os.path.basename(
            os.path.normpath(os.path.dirname(self.linter.current_file)))
        if (base_dirname != 'tests' and isinstance(node, astroid.Call)
                and isinstance(node.func, astroid.Attribute)
                and node.func.attrname == 'message_post'):
            for arg in itertools.chain(node.args, node.keywords or []):
                if isinstance(arg, astroid.Keyword):
                    keyword = arg.arg
                    value = arg.value
                else:
                    keyword = ''
                    value = arg
                if keyword and keyword not in ('subject', 'body'):
                    continue
                as_string = ''
                # case: message_post(body='String')
                if isinstance(value, astroid.Const):
                    as_string = value.as_string()
                # case: message_post(body='String %s' % (...))
                elif (isinstance(value, astroid.BinOp) and value.op == '%'
                      and isinstance(value.left, astroid.Const)
                      # The right part is translatable only if it's a
                      # function or a list of functions
                      and not (isinstance(value.right, (
                          astroid.Call, astroid.Tuple, astroid.List)) and all([
                              isinstance(child, astroid.Call)
                              for child in getattr(value.right, 'elts', [])
                          ]))):
                    as_string = value.left.as_string()
                # case: message_post(body='String {...}'.format(...))
                elif (isinstance(value, astroid.Call)
                      and isinstance(value.func, astroid.Attribute)
                      and isinstance(value.func.expr, astroid.Const)
                      and value.func.attrname == 'format'):
                    as_string = value.func.expr.as_string()
                if as_string:
                    keyword = keyword and '%s=' % keyword
                    self.add_message('translation-required',
                                     node=node,
                                     args=('message_post', keyword, as_string))

        # Call _(...) with variables into the term to be translated
        if (isinstance(node.func, astroid.Name) and node.func.name == '_'
                and node.args):
            wrong = ''
            right = ''
            arg = node.args[0]
            # case: _('...' % (variables))
            if isinstance(arg, astroid.BinOp) and arg.op == '%':
                wrong = '%s %% %s' % (arg.left.as_string(),
                                      arg.right.as_string())
                right = '_(%s) %% %s' % (arg.left.as_string(),
                                         arg.right.as_string())
            # Case: _('...'.format(variables))
            elif (isinstance(arg, astroid.Call)
                  and isinstance(arg.func, astroid.Attribute)
                  and isinstance(arg.func.expr, astroid.Const)
                  and arg.func.attrname == 'format'):
                self.add_message('str-format-used', node=node)
                wrong = arg.as_string()
                params_as_string = ', '.join([
                    x.as_string()
                    for x in itertools.chain(arg.args, arg.keywords or [])
                ])
                right = '_(%s).format(%s)' % (arg.func.expr.as_string(),
                                              params_as_string)
            if wrong and right:
                self.add_message('translation-contains-variable',
                                 node=node,
                                 args=(wrong, right))

            # translation-positional-used: Check "string to translate"
            # to check "%s %s..." used where the position can't be changed
            str2translate = arg.as_string()
            printf_args = (misc.WrapperModuleChecker.
                           _get_printf_str_args_kwargs(str2translate))
            if isinstance(printf_args, tuple) and len(printf_args) >= 2:
                # Return tuple for %s and dict for %(varname)s
                # Check just the following cases "%s %s..."
                self.add_message('translation-positional-used',
                                 node=node,
                                 args=(str2translate, ))

        # SQL Injection
        if self._check_sql_injection_risky(node):
            self.add_message('sql-injection', node=node)

        # external-request-timeout
        lib_alias = self.get_func_lib(node.func)
        # Use dict "self._from_imports" to know the source library of the method
        lib_original = self._from_imports.get(lib_alias) or lib_alias
        func_name = self.get_func_name(node.func)
        lib_original_func_name = (
            # If it using "requests.request()"
            "%s.%s" % (lib_original, func_name) if lib_original
            # If it using "from requests import request;request()"
            else self._from_imports.get(func_name))
        if lib_original_func_name in self.config.external_request_timeout_methods:
            for argument in misc.join_node_args_kwargs(node):
                if not isinstance(argument, astroid.Keyword):
                    continue
                if argument.arg == 'timeout':
                    break
            else:
                self.add_message('external-request-timeout',
                                 node=node,
                                 args=(lib_original_func_name, ))
Example #51
0
    def visit_binop(self, node):
        if node.op != '%':
            return
        left = node.left
        args = node.right

        if not (isinstance(left, astroid.Const)
                and isinstance(left.value, six.string_types)):
            return
        format_string = left.value
        try:
            required_keys, required_num_args = \
                utils.parse_format_string(format_string)
        except utils.UnsupportedFormatCharacter as e:
            c = format_string[e.index]
            self.add_message('bad-format-character',
                             node=node, args=(c, ord(c), e.index))
            return
        except utils.IncompleteFormatString:
            self.add_message('truncated-format-string', node=node)
            return
        if required_keys and required_num_args:
            # The format string uses both named and unnamed format
            # specifiers.
            self.add_message('mixed-format-string', node=node)
        elif required_keys:
            # The format string uses only named format specifiers.
            # Check that the RHS of the % operator is a mapping object
            # that contains precisely the set of keys required by the
            # format string.
            if isinstance(args, astroid.Dict):
                keys = set()
                unknown_keys = False
                for k, _ in args.items:
                    if isinstance(k, astroid.Const):
                        key = k.value
                        if isinstance(key, six.string_types):
                            keys.add(key)
                        else:
                            self.add_message('bad-format-string-key',
                                             node=node, args=key)
                    else:
                        # One of the keys was something other than a
                        # constant.  Since we can't tell what it is,
                        # supress checks for missing keys in the
                        # dictionary.
                        unknown_keys = True
                if not unknown_keys:
                    for key in required_keys:
                        if key not in keys:
                            self.add_message('missing-format-string-key',
                                             node=node, args=key)
                for key in keys:
                    if key not in required_keys:
                        self.add_message('unused-format-string-key',
                                         node=node, args=key)
            elif isinstance(args, OTHER_NODES + (astroid.Tuple,)):
                type_name = type(args).__name__
                self.add_message('format-needs-mapping',
                                 node=node, args=type_name)
            # else:
                # The RHS of the format specifier is a name or
                # expression.  It may be a mapping object, so
                # there's nothing we can check.
        else:
            # The format string uses only unnamed format specifiers.
            # Check that the number of arguments passed to the RHS of
            # the % operator matches the number required by the format
            # string.
            if isinstance(args, astroid.Tuple):
                rhs_tuple = utils.safe_infer(args)
                num_args = None
                if rhs_tuple not in (None, astroid.Uninferable):
                    num_args = len(rhs_tuple.elts)
            elif isinstance(args, OTHER_NODES + (astroid.Dict, astroid.DictComp)):
                num_args = 1
            else:
                # The RHS of the format specifier is a name or
                # expression.  It could be a tuple of unknown size, so
                # there's nothing we can check.
                num_args = None
            if num_args is not None:
                if num_args > required_num_args:
                    self.add_message('too-many-format-args', node=node)
                elif num_args < required_num_args:
                    self.add_message('too-few-format-args', node=node)
Example #52
0
    def visit_call(self, node):
        infer_node = utils.safe_infer(node.func)
        if utils.is_builtin_object(infer_node) and infer_node.name == 'print':
            self.add_message('print-used', node=node)
        if ('fields' == self.get_func_lib(node.func) and
                isinstance(node.parent, astroid.Assign) and
                isinstance(node.parent.parent, astroid.ClassDef)):
            args = misc.join_node_args_kwargs(node)
            index = 0
            field_name = ''
            if (isinstance(node.parent, astroid.Assign) and
                    node.parent.targets and
                    isinstance(node.parent.targets[0], astroid.AssignName)):
                field_name = (node.parent.targets[0].name
                              .replace('_', ' '))
            for argument in args:
                argument_aux = argument
                # Check this 'name = fields.Char("name")'
                if (isinstance(argument, astroid.Const) and
                    (index ==
                     FIELDS_METHOD.get(argument.parent.func.attrname, 0)) and
                    (argument.value in
                     [field_name.capitalize(), field_name.title()])):
                    self.add_message('attribute-string-redundant', node=node)
                if isinstance(argument, astroid.Keyword):
                    argument_aux = argument.value
                    deprecated = self.config.deprecated_field_parameters
                    if argument.arg in ['compute', 'search', 'inverse'] and \
                            isinstance(argument_aux, astroid.Const) and \
                            isinstance(argument_aux.value, string_types) and \
                            not argument_aux.value.startswith(
                                '_' + argument.arg + '_'):
                        self.add_message('method-' + argument.arg,
                                         node=argument_aux)
                    # Check if the param string is equal to the name
                    #   of variable
                    elif argument.arg == 'string' and \
                        (isinstance(argument_aux, astroid.Const) and
                         argument_aux.value in
                         [field_name.capitalize(), field_name.title()]):
                        self.add_message(
                            'attribute-string-redundant', node=node)
                    elif (argument.arg in deprecated):
                        self.add_message(
                            'renamed-field-parameter', node=node,
                            args=(argument.arg, deprecated[argument.arg])
                        )
                if isinstance(argument_aux, astroid.Call) and \
                        isinstance(argument_aux.func, astroid.Name) and \
                        argument_aux.func.name == '_':
                    self.add_message('translation-field', node=argument_aux)
                index += 1
        # Check cr.commit()
        if isinstance(node, astroid.Call) and \
                isinstance(node.func, astroid.Attribute) and \
                node.func.attrname == 'commit' and \
                self.get_cursor_name(node.func) in self.config.cursor_expr:
            self.add_message('invalid-commit', node=node)

        # Call the message_post()
        if (isinstance(node, astroid.Call) and
                isinstance(node.func, astroid.Attribute) and
                node.func.attrname == 'message_post'):
            for arg in itertools.chain(node.args, node.keywords or []):
                if isinstance(arg, astroid.Keyword):
                    keyword = arg.arg
                    value = arg.value
                else:
                    keyword = ''
                    value = arg
                if keyword and keyword not in ('subject', 'body'):
                    continue
                as_string = ''
                # case: message_post(body='String')
                if isinstance(value, astroid.Const):
                    as_string = value.as_string()
                # case: message_post(body='String %s' % (...))
                elif (isinstance(value, astroid.BinOp)
                        and value.op == '%'
                        and isinstance(value.left, astroid.Const)
                        # The right part is translatable only if it's a
                        # function or a list of functions
                        and not (
                            isinstance(value.right, (
                                astroid.Call, astroid.Tuple, astroid.List))
                            and all([
                                isinstance(child, astroid.Call)
                                for child in getattr(value.right, 'elts', [])
                            ]))):
                    as_string = value.left.as_string()
                # case: message_post(body='String {...}'.format(...))
                elif (isinstance(value, astroid.Call)
                        and isinstance(value.func, astroid.Attribute)
                        and isinstance(value.func.expr, astroid.Const)
                        and value.func.attrname == 'format'):
                    as_string = value.func.expr.as_string()
                if as_string:
                    keyword = keyword and '%s=' % keyword
                    self.add_message(
                        'translation-required', node=node,
                        args=('message_post', keyword, as_string))

        # Call _(...) with variables into the term to be translated
        if (isinstance(node.func, astroid.Name)
                and node.func.name == '_'
                and node.args):
            wrong = ''
            right = ''
            arg = node.args[0]
            # case: _('...' % (variables))
            if isinstance(arg, astroid.BinOp) and arg.op == '%':
                wrong = '%s %% %s' % (
                    arg.left.as_string(), arg.right.as_string())
                right = '_(%s) %% %s' % (
                    arg.left.as_string(), arg.right.as_string())
            # Case: _('...'.format(variables))
            elif (isinstance(arg, astroid.Call)
                    and isinstance(arg.func, astroid.Attribute)
                    and isinstance(arg.func.expr, astroid.Const)
                    and arg.func.attrname == 'format'):
                self.add_message('str-format-used', node=node)
                wrong = arg.as_string()
                params_as_string = ', '.join([
                    x.as_string()
                    for x in itertools.chain(arg.args, arg.keywords or [])])
                right = '_(%s).format(%s)' % (
                    arg.func.expr.as_string(), params_as_string)
            if wrong and right:
                self.add_message(
                    'translation-contains-variable', node=node,
                    args=(wrong, right))

            # translation-positional-used: Check "string to translate"
            # to check "%s %s..." used where the position can't be changed
            str2translate = arg.as_string()
            printf_args = (
                misc.WrapperModuleChecker.
                _get_printf_str_args_kwargs(str2translate))
            if isinstance(printf_args, tuple) and len(printf_args) >= 2:
                # Return tuple for %s and dict for %(varname)s
                # Check just the following cases "%s %s..."
                self.add_message('translation-positional-used',
                                 node=node, args=(str2translate,))

        # SQL Injection
        if isinstance(node, astroid.Call) and node.args and \
                isinstance(node.func, astroid.Attribute) and \
                node.func.attrname in ('execute', 'executemany') and \
                self.get_cursor_name(node.func) in self.config.cursor_expr:

            first_arg = node.args[0]

            risky = self._check_node_for_sqli_risk(first_arg)
            if not risky:
                for node_assignation in self._get_assignation_nodes(first_arg):
                    risky = self._check_node_for_sqli_risk(node_assignation)
                    if risky:
                        break

            if risky:
                self.add_message('sql-injection', node=node)
Example #53
0
    def _check_uninferable_callfunc(self, node):
        """
        Check that the given uninferable CallFunc node does not
        call an actual function.
        """
        if not isinstance(node.func, astroid.Getattr):
            return

        # Look for properties. First, obtain
        # the lhs of the Getattr node and search the attribute
        # there. If that attribute is a property or a subclass of properties,
        # then most likely it's not callable.

        # TODO: since astroid doesn't understand descriptors very well
        # we will not handle them here, right now.

        expr = node.func.expr
        klass = safe_infer(expr)
        if (klass is None or klass is astroid.YES
                or not isinstance(klass, astroid.Instance)):
            return

        try:
            attrs = klass._proxied.getattr(node.func.attrname)
        except astroid.NotFoundError:
            return

        stop_checking = False
        for attr in attrs:
            if attr is astroid.YES:
                continue
            if stop_checking:
                break
            if not isinstance(attr, astroid.Function):
                continue

            # Decorated, see if it is decorated with a property
            if not attr.decorators:
                continue
            for decorator in attr.decorators.nodes:
                if not isinstance(decorator, astroid.Name):
                    continue
                try:
                    for infered in decorator.infer():
                        property_like = False
                        if isinstance(infered, astroid.Class):
                            if (infered.root().name == BUILTINS
                                    and infered.name == 'property'):
                                property_like = True
                            else:
                                for ancestor in infered.ancestors():
                                    if (ancestor.name == 'property' and
                                            ancestor.root().name == BUILTINS):
                                        property_like = True
                                        break
                            if property_like:
                                self.add_message('not-callable',
                                                 node=node,
                                                 args=node.func.as_string())
                                stop_checking = True
                                break
                except InferenceError:
                    pass
                if stop_checking:
                    break
Example #54
0
    def visit_call(self, node):
        """check that called functions/methods are inferred to callable objects,
        and that the arguments passed to the function match the parameters in
        the inferred function's definition
        """
        # Build the set of keyword arguments, checking for duplicate keywords,
        # and count the positional arguments.
        call_site = astroid.arguments.CallSite.from_call(node)
        num_positional_args = len(call_site.positional_arguments)
        keyword_args = list(call_site.keyword_arguments.keys())

        # Determine if we don't have a context for our call and we use variadics.
        if isinstance(node.scope(), astroid.FunctionDef):
            has_no_context_positional_variadic = _no_context_variadic_positional(node)
            has_no_context_keywords_variadic = _no_context_variadic_keywords(node)
        else:
            has_no_context_positional_variadic = has_no_context_keywords_variadic = False

        called = safe_infer(node.func)
        # only function, generator and object defining __call__ are allowed
        if called and not called.callable():
            self.add_message('not-callable', node=node,
                             args=node.func.as_string())

        self._check_uninferable_callfunc(node)

        try:
            called, implicit_args, callable_name = _determine_callable(called)
        except ValueError:
            # Any error occurred during determining the function type, most of
            # those errors are handled by different warnings.
            return

        num_positional_args += implicit_args
        if called.args.args is None:
            # Built-in functions have no argument information.
            return

        if len(called.argnames()) != len(set(called.argnames())):
            # Duplicate parameter name (see duplicate-argument).  We can't really
            # make sense of the function call in this case, so just return.
            return

        # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
        for keyword in call_site.duplicated_keywords:
            self.add_message('repeated-keyword',
                             node=node, args=(keyword, ))

        if call_site.has_invalid_arguments() or call_site.has_invalid_keywords():
            # Can't make sense of this.
            return

        # Analyze the list of formal parameters.
        num_mandatory_parameters = len(called.args.args) - len(called.args.defaults)
        parameters = []
        parameter_name_to_index = {}
        for i, arg in enumerate(called.args.args):
            if isinstance(arg, astroid.Tuple):
                name = None
                # Don't store any parameter names within the tuple, since those
                # are not assignable from keyword arguments.
            else:
                assert isinstance(arg, astroid.AssignName)
                # This occurs with:
                #    def f( (a), (b) ): pass
                name = arg.name
                parameter_name_to_index[name] = i
            if i >= num_mandatory_parameters:
                defval = called.args.defaults[i - num_mandatory_parameters]
            else:
                defval = None
            parameters.append([(name, defval), False])

        kwparams = {}
        for i, arg in enumerate(called.args.kwonlyargs):
            if isinstance(arg, astroid.Keyword):
                name = arg.arg
            else:
                assert isinstance(arg, astroid.AssignName)
                name = arg.name
            kwparams[name] = [called.args.kw_defaults[i], False]

        # Match the supplied arguments against the function parameters.

        # 1. Match the positional arguments.
        for i in range(num_positional_args):
            if i < len(parameters):
                parameters[i][1] = True
            elif called.args.vararg is not None:
                # The remaining positional arguments get assigned to the *args
                # parameter.
                break
            else:
                # Too many positional arguments.
                self.add_message('too-many-function-args',
                                 node=node, args=(callable_name,))
                break

        # 2. Match the keyword arguments.
        for keyword in keyword_args:
            if keyword in parameter_name_to_index:
                i = parameter_name_to_index[keyword]
                if parameters[i][1]:
                    # Duplicate definition of function parameter.

                    # Might be too hardcoded, but this can actually
                    # happen when using str.format and `self` is passed
                    # by keyword argument, as in `.format(self=self)`.
                    # It's perfectly valid to so, so we're just skipping
                    # it if that's the case.
                    if not (keyword == 'self' and called.qname() == STR_FORMAT):
                        self.add_message('redundant-keyword-arg',
                                         node=node, args=(keyword, callable_name))
                else:
                    parameters[i][1] = True
            elif keyword in kwparams:
                if kwparams[keyword][1]:  # XXX is that even possible?
                    # Duplicate definition of function parameter.
                    self.add_message('redundant-keyword-arg', node=node,
                                     args=(keyword, callable_name))
                else:
                    kwparams[keyword][1] = True
            elif called.args.kwarg is not None:
                # The keyword argument gets assigned to the **kwargs parameter.
                pass
            else:
                # Unexpected keyword argument.
                self.add_message('unexpected-keyword-arg', node=node,
                                 args=(keyword, callable_name))

        # 3. Match the **kwargs, if any.
        if node.kwargs:
            for i, [(name, defval), assigned] in enumerate(parameters):
                # Assume that *kwargs provides values for all remaining
                # unassigned named parameters.
                if name is not None:
                    parameters[i][1] = True
                else:
                    # **kwargs can't assign to tuples.
                    pass

        # Check that any parameters without a default have been assigned
        # values.
        for [(name, defval), assigned] in parameters:
            if (defval is None) and not assigned:
                if name is None:
                    display_name = '<tuple>'
                else:
                    display_name = repr(name)
                # TODO(cpopa): this should be removed after PyCQA/astroid/issues/177
                if not has_no_context_positional_variadic:
                    self.add_message('no-value-for-parameter', node=node,
                                     args=(display_name, callable_name))

        for name in kwparams:
            defval, assigned = kwparams[name]
            if defval is None and not assigned and not has_no_context_keywords_variadic:
                self.add_message('missing-kwoa', node=node,
                                 args=(name, callable_name))
Example #55
0
    def visit_index(self, node):
        if not node.parent or not hasattr(node.parent, "value"):
            return
        # Look for index operations where the parent is a sequence type.
        # If the types can be determined, only allow indices to be int,
        # slice or instances with __index__.
        parent_type = safe_infer(node.parent.value)
        if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)):
            return
        if not has_known_bases(parent_type):
            return

        # Determine what method on the parent this index will use
        # The parent of this node will be a Subscript, and the parent of that
        # node determines if the Subscript is a get, set, or delete operation.
        if node.parent.ctx is astroid.Store:
            methodname = '__setitem__'
        elif node.parent.ctx is astroid.Del:
            methodname = '__delitem__'
        else:
            methodname = '__getitem__'

        # Check if this instance's __getitem__, __setitem__, or __delitem__, as
        # appropriate to the statement, is implemented in a builtin sequence
        # type. This way we catch subclasses of sequence types but skip classes
        # that override __getitem__ and which may allow non-integer indices.
        try:
            methods = parent_type.getattr(methodname)
            if methods is astroid.YES:
                return
            itemmethod = methods[0]
        except (exceptions.NotFoundError, IndexError):
            return
        if not isinstance(itemmethod, astroid.FunctionDef):
            return
        if itemmethod.root().name != BUILTINS:
            return
        if not itemmethod.parent:
            return
        if itemmethod.parent.name not in SEQUENCE_TYPES:
            return

        # For ExtSlice objects coming from visit_extslice, no further
        # inference is necessary, since if we got this far the ExtSlice
        # is an error.
        if isinstance(node, astroid.ExtSlice):
            index_type = node
        else:
            index_type = safe_infer(node)
        if index_type is None or index_type is astroid.YES:
            return
        # Constants must be of type int
        if isinstance(index_type, astroid.Const):
            if isinstance(index_type.value, int):
                return
        # Instance values must be int, slice, or have an __index__ method
        elif isinstance(index_type, astroid.Instance):
            if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
                return
            try:
                index_type.getattr('__index__')
                return
            except exceptions.NotFoundError:
                pass
        elif isinstance(index_type, astroid.Slice):
            # Delegate to visit_slice. A slice can be present
            # here after inferring the index node, which could
            # be a `slice(...)` call for instance.
            return self.visit_slice(index_type)

        # Anything else is an error
        self.add_message('invalid-sequence-index', node=node)
    def _check_use_maxsplit_arg(self, node: astroid.Call) -> None:
        """Add message when accessing first or last elements of a str.split() or str.rsplit()."""

        # Check if call is split() or rsplit()
        if not (isinstance(node.func, astroid.Attribute)
                and node.func.attrname in ("split", "rsplit") and isinstance(
                    utils.safe_infer(node.func), astroid.BoundMethod)):
            return

        try:
            utils.get_argument_from_call(node, 0, "sep")
        except utils.NoSuchArgumentError:
            return

        try:
            # Ignore if maxsplit arg has been set
            utils.get_argument_from_call(node, 1, "maxsplit")
            return
        except utils.NoSuchArgumentError:
            pass

        if isinstance(node.parent, astroid.Subscript):
            try:
                subscript_value = utils.get_subscript_const_value(
                    node.parent).value
            except utils.InferredTypeError:
                return

            # Check for cases where variable (Name) subscripts may be mutated within a loop
            if isinstance(node.parent.slice, astroid.Name):
                # Check if loop present within the scope of the node
                scope = node.scope()
                for loop_node in scope.nodes_of_class(
                    (astroid.For, astroid.While)):
                    loop_node = cast(astroid.node_classes.NodeNG, loop_node)
                    if not loop_node.parent_of(node):
                        continue

                    # Check if var is mutated within loop (Assign/AugAssign)
                    for assignment_node in loop_node.nodes_of_class(
                            astroid.AugAssign):
                        assignment_node = cast(astroid.AugAssign,
                                               assignment_node)
                        if node.parent.slice.name == assignment_node.target.name:
                            return
                    for assignment_node in loop_node.nodes_of_class(
                            astroid.Assign):
                        assignment_node = cast(astroid.Assign, assignment_node)
                        if node.parent.slice.name in [
                                n.name for n in assignment_node.targets
                        ]:
                            return

            if subscript_value in (-1, 0):
                fn_name = node.func.attrname
                new_fn = "rsplit" if subscript_value == -1 else "split"
                new_name = (
                    node.func.as_string().rsplit(fn_name, maxsplit=1)[0] +
                    new_fn +
                    f"({node.args[0].as_string()}, maxsplit=1)[{subscript_value}]"
                )
                self.add_message("use-maxsplit-arg",
                                 node=node,
                                 args=(new_name, ))
Example #57
0
    def _check_new_format_specifiers(self, node, fields, named):
        """
        Check attribute and index access in the format
        string ("{0.a}" and "{0[a]}").
        """
        for key, specifiers in fields:
            # Obtain the argument. If it can't be obtained
            # or inferred, skip this check.
            if key == "":
                # {[0]} will have an unnamed argument, defaulting
                # to 0. It will not be present in `named`, so use the value
                # 0 for it.
                key = 0
            if isinstance(key, numbers.Number):
                try:
                    argname = utils.get_argument_from_call(node, key)
                except utils.NoSuchArgumentError:
                    continue
            else:
                if key not in named:
                    continue
                argname = named[key]
            if argname in (astroid.Uninferable, None):
                continue
            try:
                argument = utils.safe_infer(argname)
            except astroid.InferenceError:
                continue
            if not specifiers or not argument:
                # No need to check this key if it doesn't
                # use attribute / item access
                continue
            if argument.parent and isinstance(argument.parent, astroid.Arguments):
                # Ignore any object coming from an argument,
                # because we can't infer its value properly.
                continue
            previous = argument
            parsed = []
            for is_attribute, specifier in specifiers:
                if previous is astroid.Uninferable:
                    break
                parsed.append((is_attribute, specifier))
                if is_attribute:
                    try:
                        previous = previous.getattr(specifier)[0]
                    except astroid.NotFoundError:
                        if (
                            hasattr(previous, "has_dynamic_getattr")
                            and previous.has_dynamic_getattr()
                        ):
                            # Don't warn if the object has a custom __getattr__
                            break
                        path = get_access_path(key, parsed)
                        self.add_message(
                            "missing-format-attribute",
                            args=(specifier, path),
                            node=node,
                        )
                        break
                else:
                    warn_error = False
                    if hasattr(previous, "getitem"):
                        try:
                            previous = previous.getitem(astroid.Const(specifier))
                        except (
                            astroid.AstroidIndexError,
                            astroid.AstroidTypeError,
                            astroid.AttributeInferenceError,
                        ):
                            warn_error = True
                        except astroid.InferenceError:
                            break
                        if previous is astroid.Uninferable:
                            break
                    else:
                        try:
                            # Lookup __getitem__ in the current node,
                            # but skip further checks, because we can't
                            # retrieve the looked object
                            previous.getattr("__getitem__")
                            break
                        except astroid.NotFoundError:
                            warn_error = True
                    if warn_error:
                        path = get_access_path(key, parsed)
                        self.add_message(
                            "invalid-format-index", args=(specifier, path), node=node
                        )
                        break

                try:
                    previous = next(previous.infer())
                except astroid.InferenceError:
                    # can't check further if we can't infer it
                    break
Example #58
0
 def _looks_like_infinite_iterator(param):
     inferred = utils.safe_infer(param)
     if inferred is not None or inferred is not astroid.Uninferable:
         return inferred.qname() in KNOWN_INFINITE_ITERATORS
     return False
Example #59
0
def _get_raise_target(node):
    if isinstance(node.exc, nodes.Call):
        func = node.exc.func
        if isinstance(func, (nodes.Name, nodes.Attribute)):
            return utils.safe_infer(func)
    return None
Example #60
0
    def _detect_replacable_format_call(self, node: nodes.Const) -> None:
        """Check whether a string is used in a call to format() or '%' and whether it
        can be replaced by an f-string
        """
        if (isinstance(node.parent, nodes.Attribute)
                and node.parent.attrname == "format"):
            # Don't warn on referencing / assigning .format without calling it
            if not isinstance(node.parent.parent, nodes.Call):
                return

            if node.parent.parent.args:
                for arg in node.parent.parent.args:
                    # If star expressions with more than 1 element are being used
                    if isinstance(arg, nodes.Starred):
                        inferred = utils.safe_infer(arg.value)
                        if (isinstance(inferred, astroid.List)
                                and len(inferred.elts) > 1):
                            return
                    # Backslashes can't be in f-string expressions
                    if "\\" in arg.as_string():
                        return

            elif node.parent.parent.keywords:
                keyword_args = [
                    i[0]
                    for i in utils.parse_format_method_string(node.value)[0]
                ]
                for keyword in node.parent.parent.keywords:
                    # If keyword is used multiple times
                    if keyword_args.count(keyword.arg) > 1:
                        return

                    keyword = utils.safe_infer(keyword.value)

                    # If lists of more than one element are being unpacked
                    if isinstance(keyword, nodes.Dict):
                        if len(keyword.items) > 1 and len(keyword_args) > 1:
                            return

            # If all tests pass, then raise message
            self.add_message(
                "consider-using-f-string",
                node=node,
                line=node.lineno,
                col_offset=node.col_offset,
            )

        elif isinstance(node.parent, nodes.BinOp) and node.parent.op == "%":
            # Backslashes can't be in f-string expressions
            if "\\" in node.parent.right.as_string():
                return

            inferred_right = utils.safe_infer(node.parent.right)

            # If dicts or lists of length > 1 are used
            if isinstance(inferred_right, nodes.Dict):
                if len(inferred_right.items) > 1:
                    return
            elif isinstance(inferred_right, nodes.List):
                if len(inferred_right.elts) > 1:
                    return

            # If all tests pass, then raise message
            self.add_message(
                "consider-using-f-string",
                node=node,
                line=node.lineno,
                col_offset=node.col_offset,
            )