예제 #1
0
def _break_check(context, value_scope, flow_scope, node):
    reachable = REACHABLE
    if flow_scope.type == 'if_stmt':
        if flow_scope.is_node_after_else(node):
            for check_node in flow_scope.get_test_nodes():
                reachable = _check_if(context, check_node)
                if reachable in (REACHABLE, UNSURE):
                    break
            reachable = reachable.invert()
        else:
            flow_node = flow_scope.get_corresponding_test_node(node)
            if flow_node is not None:
                reachable = _check_if(context, flow_node)
    elif flow_scope.type in ('try_stmt', 'while_stmt'):
        return UNSURE

    # Only reachable branches need to be examined further.
    if reachable in (UNREACHABLE, UNSURE):
        return reachable

    if value_scope != flow_scope and value_scope != flow_scope.parent:
        flow_scope = get_parent_scope(flow_scope, include_flows=True)
        return reachable & _break_check(context, value_scope, flow_scope, node)
    else:
        return reachable
예제 #2
0
        def from_scope_node(scope_node, is_nested=True):
            if scope_node == self.tree_node:
                return self

            if scope_node.type in ('funcdef', 'lambdef', 'classdef'):
                return self.create_value(scope_node).as_context()
            elif scope_node.type in ('comp_for', 'sync_comp_for'):
                parent_scope = parser_utils.get_parent_scope(scope_node)
                parent_context = from_scope_node(parent_scope)
                if node.start_pos >= scope_node.children[-1].start_pos:
                    return parent_context
                return CompForContext(parent_context, scope_node)
            raise Exception("There's a scope that was not managed: %s" %
                            scope_node)
예제 #3
0
def _search_function_arguments(module_context, funcdef, string_name):
    """
    Returns a list of param names.
    """
    compare_node = funcdef
    if string_name == '__init__':
        cls = get_parent_scope(funcdef)
        if cls.type == 'classdef':
            string_name = cls.name.value
            compare_node = cls

    found_arguments = False
    i = 0
    inference_state = module_context.inference_state

    if settings.dynamic_params_for_other_modules:
        module_contexts = get_module_contexts_containing_name(
            inference_state,
            [module_context],
            string_name,
            # Limit the amounts of files to be opened massively.
            limit_reduction=5,
        )
    else:
        module_contexts = [module_context]

    for for_mod_context in module_contexts:
        for name, trailer in _get_potential_nodes(for_mod_context,
                                                  string_name):
            i += 1

            # This is a simple way to stop Medi's dynamic param recursion
            # from going wild: The deeper Medi's in the recursion, the less
            # code should be inferred.
            if i * inference_state.dynamic_params_depth > MAX_PARAM_SEARCHES:
                return

            random_context = for_mod_context.create_context(name)
            for arguments in _check_name_for_execution(inference_state,
                                                       random_context,
                                                       compare_node, name,
                                                       trailer):
                found_arguments = True
                yield arguments

        # If there are results after processing a module, we're probably
        # good to process. This is a speed optimization.
        if found_arguments:
            return
예제 #4
0
    def _check_for_additional_knowledge(self, name_or_str, name_context,
                                        position):
        name_context = name_context or self
        # Add isinstance and other if/assert knowledge.
        if isinstance(name_or_str, Name) and not name_context.is_instance():
            flow_scope = name_or_str
            base_nodes = [name_context.tree_node]

            if any(b.type in ('comp_for', 'sync_comp_for')
                   for b in base_nodes):
                return NO_VALUES
            from medi.inference.finder import check_flow_information
            while True:
                flow_scope = get_parent_scope(flow_scope, include_flows=True)
                n = check_flow_information(name_context, flow_scope,
                                           name_or_str, position)
                if n is not None:
                    return n
                if flow_scope in base_nodes:
                    break
        return NO_VALUES
예제 #5
0
def reachability_check(context, value_scope, node, origin_scope=None):
    if is_big_annoying_library(context) \
            or not context.inference_state.flow_analysis_enabled:
        return UNSURE

    first_flow_scope = get_parent_scope(node, include_flows=True)
    if origin_scope is not None:
        origin_flow_scopes = list(_get_flow_scopes(origin_scope))
        node_flow_scopes = list(_get_flow_scopes(node))

        branch_matches = True
        for flow_scope in origin_flow_scopes:
            if flow_scope in node_flow_scopes:
                node_keyword = get_flow_branch_keyword(flow_scope, node)
                origin_keyword = get_flow_branch_keyword(flow_scope, origin_scope)
                branch_matches = node_keyword == origin_keyword
                if flow_scope.type == 'if_stmt':
                    if not branch_matches:
                        return UNREACHABLE
                elif flow_scope.type == 'try_stmt':
                    if not branch_matches and origin_keyword == 'else' \
                            and node_keyword == 'except':
                        return UNREACHABLE
                if branch_matches:
                    break

        # Direct parents get resolved, we filter scopes that are separate
        # branches.  This makes sense for autocompletion and static analysis.
        # For actual Python it doesn't matter, because we're talking about
        # potentially unreachable code.
        # e.g. `if 0:` would cause all name lookup within the flow make
        # unaccessible. This is not a "problem" in Python, because the code is
        # never called. In Medi though, we still want to infer types.
        while origin_scope is not None:
            if first_flow_scope == origin_scope and branch_matches:
                return REACHABLE
            origin_scope = origin_scope.parent

    return _break_check(context, value_scope, first_flow_scope, node)
예제 #6
0
def tree_name_to_values(inference_state, context, tree_name):
    value_set = NO_VALUES
    module_node = context.get_root_context().tree_node
    # First check for annotations, like: `foo: int = 3`
    if module_node is not None:
        names = module_node.get_used_names().get(tree_name.value, [])
        found_annotation = False
        for name in names:
            expr_stmt = name.parent

            if expr_stmt.type == "expr_stmt" and expr_stmt.children[1].type == "annassign":
                correct_scope = parser_utils.get_parent_scope(name) == context.tree_node
                if correct_scope:
                    found_annotation = True
                    value_set |= annotation.infer_annotation(
                        context, expr_stmt.children[1].children[1]
                    ).execute_annotation()
        if found_annotation:
            return value_set

    types = []
    node = tree_name.get_definition(import_name_always=True, include_setitem=True)
    if node is None:
        node = tree_name.parent
        if node.type == 'global_stmt':
            c = context.create_context(tree_name)
            if c.is_module():
                # In case we are already part of the module, there is no point
                # in looking up the global statement anymore, because it's not
                # valid at that point anyway.
                return NO_VALUES
            # For global_stmt lookups, we only need the first possible scope,
            # which means the function itself.
            filter = next(c.get_filters())
            names = filter.get(tree_name.value)
            return ValueSet.from_sets(name.infer() for name in names)
        elif node.type not in ('import_from', 'import_name'):
            c = context.create_context(tree_name)
            return infer_atom(c, tree_name)

    typ = node.type
    if typ == 'for_stmt':
        types = annotation.find_type_from_comment_hint_for(context, node, tree_name)
        if types:
            return types
    if typ == 'with_stmt':
        types = annotation.find_type_from_comment_hint_with(context, node, tree_name)
        if types:
            return types

    if typ in ('for_stmt', 'comp_for', 'sync_comp_for'):
        try:
            types = context.predefined_names[node][tree_name.value]
        except KeyError:
            cn = ContextualizedNode(context, node.children[3])
            for_types = iterate_values(
                cn.infer(),
                contextualized_node=cn,
                is_async=node.parent.type == 'async_stmt',
            )
            n = TreeNameDefinition(context, tree_name)
            types = check_tuple_assignments(n, for_types)
    elif typ == 'expr_stmt':
        types = infer_expr_stmt(context, node, tree_name)
    elif typ == 'with_stmt':
        value_managers = context.infer_node(node.get_test_node_from_name(tree_name))
        enter_methods = value_managers.py__getattribute__(u'__enter__')
        return enter_methods.execute_with_values()
    elif typ in ('import_from', 'import_name'):
        types = imports.infer_import(context, tree_name)
    elif typ in ('funcdef', 'classdef'):
        types = _apply_decorators(context, node)
    elif typ == 'try_stmt':
        # TODO an exception can also be a tuple. Check for those.
        # TODO check for types that are not classes and add it to
        # the static analysis report.
        exceptions = context.infer_node(tree_name.get_previous_sibling().get_previous_sibling())
        types = exceptions.execute_with_values()
    elif typ == 'param':
        types = NO_VALUES
    elif typ == 'del_stmt':
        types = NO_VALUES
    else:
        raise ValueError("Should not happen. type: %s" % typ)
    return types
예제 #7
0
def _get_flow_scopes(node):
    while True:
        node = get_parent_scope(node, include_flows=True)
        if node is None or is_scope(node):
            return
        yield node
예제 #8
0
파일: helpers.py 프로젝트: yuan-xy/medi
 def is_module_scope_name(name):
     parent_scope = get_parent_scope(name)
     # async functions have an extra wrapper. Strip it.
     if parent_scope and parent_scope.type == 'async_stmt':
         parent_scope = parent_scope.parent
     return parent_scope in (module, None)