Exemplo n.º 1
0
    def check_hasattr(node, suite):
        try:
            assert suite.start_pos <= jedi_name.start_pos < suite.end_pos
            assert node.type in ('power', 'atom_expr')
            base = node.children[0]
            assert base.type == 'name' and base.value == 'hasattr'
            trailer = node.children[1]
            assert trailer.type == 'trailer'
            arglist = trailer.children[1]
            assert arglist.type == 'arglist'
            from jedi.inference.arguments import TreeArguments
            args = TreeArguments(node_context.inference_state, node_context, arglist)
            unpacked_args = list(args.unpack())
            # Arguments should be very simple
            assert len(unpacked_args) == 2

            # Check name
            key, lazy_value = unpacked_args[1]
            names = list(lazy_value.infer())
            assert len(names) == 1 and is_string(names[0])
            assert names[0].get_safe_value() == payload[1].value

            # Check objects
            key, lazy_value = unpacked_args[0]
            objects = lazy_value.infer()
            return payload[0] in objects
        except AssertionError:
            return False
Exemplo n.º 2
0
 def exact_key_items(self):
     """
     Returns a generator of tuples like dict.items(), where the key is
     resolved (as a string) and the values are still lazy values.
     """
     for key_node, value in self.get_tree_entries():
         for key in self._defining_context.infer_node(key_node):
             if is_string(key):
                 yield key.get_safe_value(), LazyTreeValue(self._defining_context, value)
Exemplo n.º 3
0
 def resolve_forward_references(value_set):
     for value in value_set:
         if is_string(value):
             from jedi.inference.gradual.annotation import _get_forward_reference_node
             node = _get_forward_reference_node(defining_context, value.get_safe_value())
             if node is not None:
                 for c in defining_context.infer_node(node):
                     yield c
         else:
             yield value
Exemplo n.º 4
0
def _paths_from_assignment(module_context, expr_stmt):
    """
    Extracts the assigned strings from an assignment that looks as follows::

        sys.path[0:0] = ['module/path', 'another/module/path']

    This function is in general pretty tolerant (and therefore 'buggy').
    However, it's not a big issue usually to add more paths to Jedi's sys_path,
    because it will only affect Jedi in very random situations and by adding
    more paths than necessary, it usually benefits the general user.
    """
    for assignee, operator in zip(expr_stmt.children[::2],
                                  expr_stmt.children[1::2]):
        try:
            assert operator in ['=', '+=']
            assert assignee.type in ('power', 'atom_expr') and \
                len(assignee.children) > 1
            c = assignee.children
            assert c[0].type == 'name' and c[0].value == 'sys'
            trailer = c[1]
            assert trailer.children[0] == '.' and trailer.children[
                1].value == 'path'
            # TODO Essentially we're not checking details on sys.path
            # manipulation. Both assigment of the sys.path and changing/adding
            # parts of the sys.path are the same: They get added to the end of
            # the current sys.path.
            """
            execution = c[2]
            assert execution.children[0] == '['
            subscript = execution.children[1]
            assert subscript.type == 'subscript'
            assert ':' in subscript.children
            """
        except AssertionError:
            continue

        cn = ContextualizedNode(module_context.create_context(expr_stmt),
                                expr_stmt)
        for lazy_value in cn.infer().iterate(cn):
            for value in lazy_value.infer():
                if is_string(value):
                    abs_path = _abs_path(module_context,
                                         value.get_safe_value())
                    if abs_path is not None:
                        yield abs_path
Exemplo n.º 5
0
def infer_annotation(context, annotation):
    """
    Inferes an annotation node. This means that it inferes the part of
    `int` here:

        foo: int = 3

    Also checks for forward references (strings)
    """
    value_set = context.infer_node(annotation)
    if len(value_set) != 1:
        debug.warning("Inferred typing index %s should lead to 1 object, "
                      " not %s" % (annotation, value_set))
        return value_set

    inferred_value = list(value_set)[0]
    if is_string(inferred_value):
        result = _get_forward_reference_node(context, inferred_value.get_safe_value())
        if result is not None:
            return context.infer_node(result)
    return value_set
Exemplo n.º 6
0
def _infer_comparison_part(inference_state, context, left, operator, right):
    l_is_num = is_number(left)
    r_is_num = is_number(right)
    if isinstance(operator, str):
        str_operator = operator
    else:
        str_operator = str(operator.value)

    if str_operator == '*':
        # for iterables, ignore * operations
        if isinstance(left, iterable.Sequence) or is_string(left):
            return ValueSet([left])
        elif isinstance(right, iterable.Sequence) or is_string(right):
            return ValueSet([right])
    elif str_operator == '+':
        if l_is_num and r_is_num or is_string(left) and is_string(right):
            return left.execute_operation(right, str_operator)
        elif _is_list(left) and _is_list(right) or _is_tuple(
                left) and _is_tuple(right):
            return ValueSet(
                [iterable.MergedArray(inference_state, (left, right))])
    elif str_operator == '-':
        if l_is_num and r_is_num:
            return left.execute_operation(right, str_operator)
    elif str_operator == '%':
        # With strings and numbers the left type typically remains. Except for
        # `int() % float()`.
        return ValueSet([left])
    elif str_operator in COMPARISON_OPERATORS:
        if left.is_compiled() and right.is_compiled():
            # Possible, because the return is not an option. Just compare.
            result = left.execute_operation(right, str_operator)
            if result:
                return result
        else:
            if str_operator in ('is', '!=', '==', 'is not'):
                operation = COMPARISON_OPERATORS[str_operator]
                bool_ = operation(left, right)
                # Only if == returns True or != returns False, we can continue.
                # There's no guarantee that they are not equal. This can help
                # in some cases, but does not cover everything.
                if (str_operator in ('is', '==')) == bool_:
                    return ValueSet([_bool_to_value(inference_state, bool_)])

            if isinstance(left, VersionInfo):
                version_info = _get_tuple_ints(right)
                if version_info is not None:
                    bool_result = compiled.access.COMPARISON_OPERATORS[
                        operator](inference_state.environment.version_info,
                                  tuple(version_info))
                    return ValueSet(
                        [_bool_to_value(inference_state, bool_result)])

        return ValueSet([
            _bool_to_value(inference_state, True),
            _bool_to_value(inference_state, False)
        ])
    elif str_operator in ('in', 'not in'):
        return NO_VALUES

    def check(obj):
        """Checks if a Jedi object is either a float or an int."""
        return isinstance(obj, TreeInstance) and \
            obj.name.string_name in ('int', 'float')

    # Static analysis, one is a number, the other one is not.
    if str_operator in ('+', '-') and l_is_num != r_is_num \
            and not (check(left) or check(right)):
        message = "TypeError: unsupported operand type(s) for +: %s and %s"
        analysis.add(context, 'type-error-operation', operator,
                     message % (left, right))

    if left.is_class() or right.is_class():
        return NO_VALUES

    method_name = operator_to_magic_method[str_operator]
    magic_methods = left.py__getattribute__(method_name)
    if magic_methods:
        result = magic_methods.execute_with_values(right)
        if result:
            return result

    if not magic_methods:
        reverse_method_name = reverse_operator_to_magic_method[str_operator]
        magic_methods = right.py__getattribute__(reverse_method_name)

        result = magic_methods.execute_with_values(left)
        if result:
            return result

    result = ValueSet([left, right])
    debug.dbg('Used operator %s resulting in %s', operator, result)
    return result
def _infer_comparison_part(inference_state, context, left, operator, right):
    l_is_num = is_number(left)
    r_is_num = is_number(right)
    if isinstance(operator, unicode):
        str_operator = operator
    else:
        str_operator = force_unicode(str(operator.value))

    if str_operator == '*':
        # for iterables, ignore * operations
        if isinstance(left, iterable.Sequence) or is_string(left):
            return ValueSet([left])
        elif isinstance(right, iterable.Sequence) or is_string(right):
            return ValueSet([right])
    elif str_operator == '+':
        if l_is_num and r_is_num or is_string(left) and is_string(right):
            return ValueSet([left.execute_operation(right, str_operator)])
        elif _is_tuple(left) and _is_tuple(right) or _is_list(
                left) and _is_list(right):
            return ValueSet(
                [iterable.MergedArray(inference_state, (left, right))])
    elif str_operator == '-':
        if l_is_num and r_is_num:
            return ValueSet([left.execute_operation(right, str_operator)])
    elif str_operator == '%':
        # With strings and numbers the left type typically remains. Except for
        # `int() % float()`.
        return ValueSet([left])
    elif str_operator in COMPARISON_OPERATORS:
        if left.is_compiled() and right.is_compiled():
            # Possible, because the return is not an option. Just compare.
            try:
                return ValueSet([left.execute_operation(right, str_operator)])
            except TypeError:
                # Could be True or False.
                pass
        else:
            if str_operator in ('is', '!=', '==', 'is not'):
                operation = COMPARISON_OPERATORS[str_operator]
                bool_ = operation(left, right)
                return ValueSet([_bool_to_value(inference_state, bool_)])

            if isinstance(left, VersionInfo):
                version_info = _get_tuple_ints(right)
                if version_info is not None:
                    bool_result = compiled.access.COMPARISON_OPERATORS[
                        operator](inference_state.environment.version_info,
                                  tuple(version_info))
                    return ValueSet(
                        [_bool_to_value(inference_state, bool_result)])

        return ValueSet([
            _bool_to_value(inference_state, True),
            _bool_to_value(inference_state, False)
        ])
    elif str_operator == 'in':
        return NO_VALUES

    def check(obj):
        """Checks if a Jedi object is either a float or an int."""
        return isinstance(obj, TreeInstance) and \
            obj.name.string_name in ('int', 'float')

    # Static analysis, one is a number, the other one is not.
    if str_operator in ('+', '-') and l_is_num != r_is_num \
            and not (check(left) or check(right)):
        message = "TypeError: unsupported operand type(s) for +: %s and %s"
        analysis.add(context, 'type-error-operation', operator,
                     message % (left, right))

    result = ValueSet([left, right])
    debug.dbg('Used operator %s resulting in %s', operator, result)
    return result