コード例 #1
0
def find_dependencies(root):
    """
    Finds all dependencies in root object based on symbol table. 
    Consider a dependecy as containing a source and a destination.


    Cases (nodes) to account for:
        1) Assign 
            -check for the Name being assigned
        2) Name being invoked
            -here it makes sense to search for name
        3) Attribute
            e.g. pdb.set_trace(), set_trace is an attribute of pdb
            -see check dependency
        4) Import
            These are needed when building a symbols table
        5) ImportFrom
            ibid
            
    Gotchas:
        1) a name being stored
            i.e. a name collisions that makes it seem like
            a dependency exists
        2) x = y = z = 3
        3) Attribute chains can be arbitrarily long
            x.y.z,
            or x().y().z(),
            or some combination thereof

    There are a lot of cases to be handled

    Arguments:
        root:- the root of the AST being analyzed
            stored as (2-tuple) with (root node, array of children )
    """
    
    symbol_table = create_symbol_table(root)

    names = []
    #Set the depth of the root node
    set_depth(root, 0)
    #Stack of nodes to visit
    stack = Stack(root)
    
    #List of (src, dest) of dependencies
    dependency_table = DTable(symbol_table=symbol_table)

    for node, children, ntype in stack:
        
        stack.check_and_push_scope()

        #A Name is being loaded, therefore 
        if ntype == "Name" and is_load(children):
            """
            """
            dependency_table.append( (stack.scopes, node))
        
        elif ntype == "Assign":
            #TODO need to add assignments and then revoke them
            #for child in children:
            #print children
            pass

            
        elif ntype == "Attribute":
            #TODO: attribute chains can be arbitrarily long
            #dep_dest = "{}.{}".format(node.value.id, node.attr)
            #print "{} => {}".format(scopes_to_str(scopes), dep_dest)

            #TODO: Can't just do dependency_table.append( (scopes, node))
            #since the unique_id function won't match the create the dep string like 
            #{node.value.id}.{node.attr}.
            #Either generalize unique_id or something else.
            
            #Don't add children
            continue
            
        set_lineno(node, children)
        #Add children to stack
        #This musn't always be performed
        for child in children[::-1]:
            set_depth(child, node.depth + 1)
            stack.append(child)

    print "dependency table is "
    print dependency_table 
コード例 #2
0
def create_symbol_table(root):
    """
    Creates a symbols table that maps each
    symbol to the scope within which it occurs.
    The symbol table has to be created in its entirety 
    first, since entities (e.g. functions, classes) can be 
    referenced before being defined.

    Similar to find_dependencies in terms of traversing
    the AST.
    TODO: refactor common stuff into separate function

    The data structure used is a hashtable where 
    names are mapped to list of scopes, i.e. a hashlist. 

    The scope can be precisely defined in terms of 
    lineno range. The alternative is to define as scope
    as, e.g. <module name>.<function name> but this can lead 
    to ambiguity since the functions etc. can be redefined.
    Also the lineno approach relies on the beginning lines
    of siblings which can lead to a larger range than actually is 
    due to whitespaces. This gets tricky because functions can 
    be used before they are defined, but not variables.  
    """

    set_depth(root, 0)
    #Initialize the stack, with the AST root
    stack = Stack(root)

    #the symbol table maps the name to the scope.
    #Any node can belong to multiple scopes, therefore this
    #is a list of scope
    symbol_table = STable()
    
    #this represents objects imported from
    #other modules
    other_modules = {}

    for node, children, ntype in stack:

        if ntype == "Import":
            #Import object has names prop which
            #is an array of names
            for name in node.names:
                #name can be the name or an alias   
                name_val = name.asname or name.name
                #insert in symbol_table                
                symbol_table[name_val] = ()

        elif ntype == "ImportFrom":
            if node.names[0].name == '*':
                try:
                    imp_mod = importlib.import_module(node.module)
                    #Add all names in imported module, except those
                    #starting with '_'
                    for name in dir(imp_mod):
                        if name[0] != '_':
                            symbol_table[name] = stack_top(scopes)

                except ImportError:
                    print "Error: local system does not have {}. Skipping!".format(node.module)
                    pass
            else:
                #TODO: store node.module
                for name in node.names:
                    #TODO: store name.name even if name.asname defined    
                    name_val = name.asname or name.name
                    symbol_table[name_val] = stack.get_scopes(src_module=node.module)

        elif ntype == "ClassDef" or ntype == "FunctionDef":   
            symbol_table[node.name] = stack.get_scopes()
        
        #NOTE: if a name is being loaded then it already exists and doesn't need
        #to be added to symbol_table
        elif ntype == "Name" and not is_load(children) and not has_global(stack.scope_tail(), node.id): 
            symbol_table[node.id] = stack.get_scopes()

        elif ntype == "arguments":
            if node.vararg: 
                symbol_table[node.vararg] = stack.get_scopes()
            if node.kwarg:
                symbol_table[node.kwarg] = stack.get_scopes()

        elif ntype == "Global":
            #add a list global vars on node on the top of  
            #the stack
            #nonlocal could be handled in similar way
            set_globals(scopes[-1], node.names)

        #set lineno property of children nodes
        set_lineno(node, children)

        for child in children[::-1]:
            #set depth of child
            set_depth(child, node.depth + 1)
            #Add children to stack
            stack.append(child)

        #Add any new scopes
        #Need to do it here since scoping_nodes are defined in their parent scope
        stack.check_and_push_scope()

    print "Symbol table is "
    print symbol_table
    return symbol_table