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)
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)
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)
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)
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
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
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)
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)
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
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
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
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
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
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
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
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
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