Ejemplo n.º 1
0
def _eval_comparison_part(evaluator, 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 ContextSet(left)
        elif isinstance(right, iterable.Sequence) or is_string(right):
            return ContextSet(right)
    elif str_operator == '+':
        if l_is_num and r_is_num or is_string(left) and is_string(right):
            return ContextSet(left.execute_operation(right, str_operator))
        elif _is_tuple(left) and _is_tuple(right) or _is_list(
                left) and _is_list(right):
            return ContextSet(iterable.MergedArray(evaluator, (left, right)))
    elif str_operator == '-':
        if l_is_num and r_is_num:
            return ContextSet(left.execute_operation(right, str_operator))
    elif str_operator == '%':
        # With strings and numbers the left type typically remains. Except for
        # `int() % float()`.
        return ContextSet(left)
    elif str_operator in COMPARISON_OPERATORS:
        if is_compiled(left) and is_compiled(right):
            # Possible, because the return is not an option. Just compare.
            try:
                return ContextSet(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 ContextSet(_bool_to_context(evaluator, bool_))

        return ContextSet(_bool_to_context(evaluator, True),
                          _bool_to_context(evaluator, False))
    elif str_operator == 'in':
        return NO_CONTEXTS

    def check(obj):
        """Checks if a Jedi object is either a float or an int."""
        return isinstance(obj, CompiledInstance) 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))

    return ContextSet(left, right)
Ejemplo n.º 2
0
def _eval_comparison_part(evaluator, 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 ContextSet(left)
        elif isinstance(right, iterable.Sequence) or is_string(right):
            return ContextSet(right)
    elif str_operator == '+':
        if l_is_num and r_is_num or is_string(left) and is_string(right):
            return ContextSet(left.execute_operation(right, str_operator))
        elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
            return ContextSet(iterable.MergedArray(evaluator, (left, right)))
    elif str_operator == '-':
        if l_is_num and r_is_num:
            return ContextSet(left.execute_operation(right, str_operator))
    elif str_operator == '%':
        # With strings and numbers the left type typically remains. Except for
        # `int() % float()`.
        return ContextSet(left)
    elif str_operator in COMPARISON_OPERATORS:
        if is_compiled(left) and is_compiled(right):
            # Possible, because the return is not an option. Just compare.
            try:
                return ContextSet(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 ContextSet(_bool_to_context(evaluator, bool_))

        return ContextSet(_bool_to_context(evaluator, True), _bool_to_context(evaluator, False))
    elif str_operator == 'in':
        return NO_CONTEXTS

    def check(obj):
        """Checks if a Jedi object is either a float or an int."""
        return isinstance(obj, CompiledInstance) 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))

    return ContextSet(left, right)
Ejemplo n.º 3
0
def _eval_comparison_part(evaluator, context, left, operator, right):
    l_is_num = is_number(left)
    r_is_num = is_number(right)
    if operator == '*':
        # for iterables, ignore * operations
        if isinstance(left, iterable.AbstractIterable) or is_string(left):
            return ContextSet(left)
        elif isinstance(right, iterable.AbstractIterable) or is_string(right):
            return ContextSet(right)
    elif operator == '+':
        if l_is_num and r_is_num or is_string(left) and is_string(right):
            return ContextSet(compiled.create(evaluator, left.obj + right.obj))
        elif _is_tuple(left) and _is_tuple(right) or _is_list(
                left) and _is_list(right):
            return ContextSet(iterable.MergedArray(evaluator, (left, right)))
    elif operator == '-':
        if l_is_num and r_is_num:
            return ContextSet(compiled.create(evaluator, left.obj - right.obj))
    elif operator == '%':
        # With strings and numbers the left type typically remains. Except for
        # `int() % float()`.
        return ContextSet(left)
    elif operator in COMPARISON_OPERATORS:
        operation = COMPARISON_OPERATORS[operator]
        if is_compiled(left) and is_compiled(right):
            # Possible, because the return is not an option. Just compare.
            left = left.obj
            right = right.obj

        try:
            result = operation(left, right)
        except TypeError:
            # Could be True or False.
            return ContextSet(compiled.create(evaluator, True),
                              compiled.create(evaluator, False))
        else:
            return ContextSet(compiled.create(evaluator, result))
    elif operator == 'in':
        return NO_CONTEXTS

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

    # Static analysis, one is a number, the other one is not.
    if 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))

    return ContextSet(left, right)
Ejemplo n.º 4
0
def _eval_comparison_part(evaluator, context, left, operator, right):
    l_is_num = is_number(left)
    r_is_num = is_number(right)
    if operator == '*':
        # for iterables, ignore * operations
        if isinstance(left, iterable.AbstractIterable) or is_string(left):
            return ContextSet(left)
        elif isinstance(right, iterable.AbstractIterable) or is_string(right):
            return ContextSet(right)
    elif operator == '+':
        if l_is_num and r_is_num or is_string(left) and is_string(right):
            return ContextSet(compiled.create(evaluator, left.obj + right.obj))
        elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
            return ContextSet(iterable.MergedArray(evaluator, (left, right)))
    elif operator == '-':
        if l_is_num and r_is_num:
            return ContextSet(compiled.create(evaluator, left.obj - right.obj))
    elif operator == '%':
        # With strings and numbers the left type typically remains. Except for
        # `int() % float()`.
        return ContextSet(left)
    elif operator in COMPARISON_OPERATORS:
        operation = COMPARISON_OPERATORS[operator]
        if is_compiled(left) and is_compiled(right):
            # Possible, because the return is not an option. Just compare.
            left = left.obj
            right = right.obj

        try:
            result = operation(left, right)
        except TypeError:
            # Could be True or False.
            return ContextSet(compiled.create(evaluator, True), compiled.create(evaluator, False))
        else:
            return ContextSet(compiled.create(evaluator, result))
    elif operator == 'in':
        return NO_CONTEXTS

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

    # Static analysis, one is a number, the other one is not.
    if 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))

    return ContextSet(left, right)
Ejemplo n.º 5
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.evaluate.arguments import TreeArguments
            args = list(TreeArguments(node_context.evaluator, node_context, arglist).unpack())
            # Arguments should be very simple
            assert len(args) == 2

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

            # Check objects
            key, lazy_context = args[0]
            objects = lazy_context.infer()
            return payload[0] in objects
        except AssertionError:
            return False
Ejemplo n.º 6
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.evaluate.arguments import TreeArguments
            args = list(TreeArguments(node_context.evaluator, node_context, arglist).unpack())
            # Arguments should be very simple
            assert len(args) == 2

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

            # Check objects
            key, lazy_context = args[0]
            objects = lazy_context.infer()
            return payload[0] in objects
        except AssertionError:
            return False
Ejemplo n.º 7
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 contexts.
     """
     for key_node, value in self._items():
         for key in self._defining_context.eval_node(key_node):
             if is_string(key):
                 yield key.get_safe_value(), LazyTreeContext(self._defining_context, value)
Ejemplo n.º 8
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 contexts.
     """
     for key_node, value in self._items():
         for key in self._defining_context.eval_node(key_node):
             if is_string(key):
                 yield key.obj, LazyTreeContext(self._defining_context, value)
Ejemplo n.º 9
0
 def resolve_forward_references(context_set):
     for context in context_set:
         if is_string(context):
             from jedi.evaluate.gradual.annotation import _get_forward_reference_node
             node = _get_forward_reference_node(defining_context, context.get_safe_value())
             if node is not None:
                 for c in defining_context.eval_node(node):
                     yield c
         else:
             yield context
Ejemplo n.º 10
0
def _fix_forward_reference(context, node):
    evaled_nodes = context.eval_node(node)
    if len(evaled_nodes) != 1:
        debug.warning("Eval'ed typing index %s should lead to 1 object, "
                      " not %s" % (node, evaled_nodes))
        return node

    evaled_context = list(evaled_nodes)[0]
    if is_string(evaled_context):
        result = _get_forward_reference_node(context, evaled_context.get_safe_value())
        if result is not None:
            return result

    return node
Ejemplo n.º 11
0
def _fix_forward_reference(context, node):
    evaled_nodes = context.eval_node(node)
    if len(evaled_nodes) != 1:
        debug.warning("Eval'ed typing index %s should lead to 1 object, "
                      " not %s" % (node, evaled_nodes))
        return node

    evaled_context = list(evaled_nodes)[0]
    if is_string(evaled_context):
        result = _get_forward_reference_node(context, evaled_context.get_safe_value())
        if result is not None:
            return result

    return node
Ejemplo n.º 12
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_context in cn.infer().iterate(cn):
            for context in lazy_context.infer():
                if is_string(context):
                    abs_path = _abs_path(module_context,
                                         context.get_safe_value())
                    if abs_path is not None:
                        yield abs_path
Ejemplo n.º 13
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_context in cn.infer().iterate(cn):
            for context in lazy_context.infer():
                if is_string(context):
                    abs_path = _abs_path(module_context, context.get_safe_value())
                    if abs_path is not None:
                        yield abs_path
Ejemplo n.º 14
0
def eval_annotation(context, annotation):
    """
    Evaluates an annotation node. This means that it evaluates the part of
    `int` here:

        foo: int = 3

    Also checks for forward references (strings)
    """
    context_set = context.eval_node(annotation)
    if len(context_set) != 1:
        debug.warning("Eval'ed typing index %s should lead to 1 object, "
                      " not %s" % (annotation, context_set))
        return context_set

    evaled_context = list(context_set)[0]
    if is_string(evaled_context):
        result = _get_forward_reference_node(context, evaled_context.get_safe_value())
        if result is not None:
            return context.eval_node(result)
    return context_set
Ejemplo n.º 15
0
def _paths_from_list_modifications(module_context, trailer1, trailer2):
    """ extract the path from either "sys.path.append" or "sys.path.insert" """
    # Guarantee that both are trailers, the first one a name and the second one
    # a function execution with at least one param.
    if not (trailer1.type == 'trailer' and trailer1.children[0] == '.'
            and trailer2.type == 'trailer' and trailer2.children[0] == '('
            and len(trailer2.children) == 3):
        return

    name = trailer1.children[1].value
    if name not in ['insert', 'append']:
        return
    arg = trailer2.children[1]
    if name == 'insert' and len(arg.children) in (3, 4):  # Possible trailing comma.
        arg = arg.children[2]

    for context in module_context.create_context(arg).eval_node(arg):
        if is_string(context):
            abs_path = _abs_path(module_context, context.get_safe_value())
            if abs_path is not None:
                yield abs_path
Ejemplo n.º 16
0
def _paths_from_list_modifications(module_context, trailer1, trailer2):
    """ extract the path from either "sys.path.append" or "sys.path.insert" """
    # Guarantee that both are trailers, the first one a name and the second one
    # a function execution with at least one param.
    if not (trailer1.type == 'trailer' and trailer1.children[0] == '.'
            and trailer2.type == 'trailer' and trailer2.children[0] == '('
            and len(trailer2.children) == 3):
        return

    name = trailer1.children[1].value
    if name not in ['insert', 'append']:
        return
    arg = trailer2.children[1]
    if name == 'insert' and len(arg.children) in (3, 4):  # Possible trailing comma.
        arg = arg.children[2]

    for context in module_context.create_context(arg).eval_node(arg):
        if is_string(context):
            abs_path = _abs_path(module_context, context.get_safe_value())
            if abs_path is not None:
                yield abs_path
Ejemplo n.º 17
0
def _fix_forward_reference(context, node):
    evaled_nodes = context.eval_node(node)
    if len(evaled_nodes) != 1:
        debug.warning("Eval'ed typing index %s should lead to 1 object, "
                      " not %s" % (node, evaled_nodes))
        return node
    evaled_node = list(evaled_nodes)[0]
    if is_string(evaled_node):
        try:
            new_node = context.evaluator.grammar.parse(
                force_unicode(evaled_node.get_safe_value()),
                start_symbol='eval_input',
                error_recovery=False)
        except ParserSyntaxError:
            debug.warning('Annotation not parsed: %s' % evaled_node)
            return node
        else:
            module = node.get_root_node()
            parser_utils.move(new_node, module.end_pos[0])
            new_node.parent = context.tree_node
            return new_node
    else:
        return node