[OpenLayers-Commits] r11006 - trunk/openlayers/tools

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Wed Jan 5 12:39:45 EST 2011


Author: tschaub
Date: 2011-01-05 09:39:44 -0800 (Wed, 05 Jan 2011)
New Revision: 11006

Modified:
   trunk/openlayers/tools/mergejs.py
   trunk/openlayers/tools/toposort.py
Log:
Simplifying the toposort method used for sorting dependencies.  The new version imposes an arbitrary sort order for circular dependencies (instead of bombing out).  The merge script now uses this new toposort.  Tools only commit.

Modified: trunk/openlayers/tools/mergejs.py
===================================================================
--- trunk/openlayers/tools/mergejs.py	2011-01-05 17:37:58 UTC (rev 11005)
+++ trunk/openlayers/tools/mergejs.py	2011-01-05 17:39:44 UTC (rev 11006)
@@ -44,6 +44,10 @@
 SUFFIX_JAVASCRIPT = ".js"
 
 RE_REQUIRE = "@requires:? (.*)\n" # TODO: Ensure in comment?
+
+class MissingImport(Exception):
+    """Exception raised when a listed import is not found in the lib."""
+
 class SourceFile:
     """
     Represents a Javascript source code file.
@@ -161,8 +165,6 @@
 
     files = {}
 
-    order = [] # List of filepaths to output, in a dependency satisfying order 
-
     ## Import file source code
     ## TODO: Do import when we walk the directories above?
     for filepath in allFiles:
@@ -179,41 +181,31 @@
     resolution_pass = 1
 
     while not complete:
-        order = [] # List of filepaths to output, in a dependency satisfying order 
-        nodes = []
-        routes = []
+        complete = True
+
         ## Resolve the dependencies
         print "Resolution pass %s... " % resolution_pass
         resolution_pass += 1 
 
         for filepath, info in files.items():
-            nodes.append(filepath)
-            for neededFilePath in info.requires:
-                routes.append((neededFilePath, filepath))
-
-        for dependencyLevel in toposort(nodes, routes):
-            for filepath in dependencyLevel:
-                order.append(filepath)
-                if not files.has_key(filepath):
-                    print "Importing: %s" % filepath
-                    fullpath = os.path.join(sourceDirectory, filepath).strip()
-                    content = open(fullpath, "U").read() # TODO: Ensure end of line @ EOF?
-                    files[filepath] = SourceFile(filepath, content) # TODO: Chop path?
-        
-
-
-        # Double check all dependencies have been met
-        complete = True
-        try:
-            for fp in order:
-                if max([order.index(rfp) for rfp in files[fp].requires] +
-                       [order.index(fp)]) != order.index(fp):
+            for path in info.requires:
+                if not files.has_key(path):
                     complete = False
-        except:
-            complete = False
+                    fullpath = os.path.join(sourceDirectory, path).strip()
+                    if os.path.exists(fullpath):
+                        print "Importing: %s" % path
+                        content = open(fullpath, "U").read() # TODO: Ensure end of line @ EOF?
+                        files[path] = SourceFile(path, content) # TODO: Chop path?
+                    else:
+                        raise MissingImport("File '%s' not found (required by '%s')." % (path, filepath))
         
-        print    
+    # create dictionary of dependencies
+    dependencies = {}
+    for filepath, info in files.items():
+        dependencies[filepath] = info.requires
 
+    print "Sorting..."
+    order = toposort(dependencies) #[x for x in toposort(dependencies)]
 
     ## Move forced first and last files to the required position
     if cfg:

Modified: trunk/openlayers/tools/toposort.py
===================================================================
--- trunk/openlayers/tools/toposort.py	2011-01-05 17:37:58 UTC (rev 11005)
+++ trunk/openlayers/tools/toposort.py	2011-01-05 17:39:44 UTC (rev 11006)
@@ -1,260 +1,35 @@
-#
-# According to <http://www.vrplumber.com/programming/> this file
-# is licensed under a BSD-style license. We only use the section
-# originally by Tim Peters.
-#
-# TODO: The use of this code needs to be okayed by someone.
-#
+"""
+toposort.py
+Sorts dictionary keys based on lists of dependencies.
+"""
 
-class RecursionError( OverflowError, ValueError ):
-    '''Unable to calculate result because of recursive structure'''
-    
+class MissingDependency(Exception):
+    """Exception raised when a listed dependency is not in the dictionary."""
 
-def sort(nodes, routes, noRecursion=1):
-    '''Passed a list of node IDs and a list of source,dest ID routes
-    attempt to create a list of stages where each sub list
-    is one stage in a process.
-    '''
-    children, parents = _buildChildrenLists(routes)
-    # first stage is those nodes
-    # having no incoming routes...
-    stage = []
-    stages = [stage]
-    taken = []
-    for node in nodes:
-        if (not parents.get(node)):
-            stage.append (node)
-    if nodes and not stage:
-        # there is no element which does not depend on
-        # some other element!!!
-        stage.append( nodes[0])
-    taken.extend( stage )
-    nodes = filter ( lambda x, l=stage: x not in l, nodes )
-    while nodes:
-        previousStageChildren = []
-        nodelen = len(nodes)
-        # second stage are those nodes
-        # which are direct children of the first stage
-        for node in stage:
-            for child in children.get (node, []):
-                if child not in previousStageChildren and child not in taken:
-                    previousStageChildren.append(child)
-                elif child in taken and noRecursion:
-                    raise RecursionError( (child, node) )
-        # unless they are children of other direct children...
-        # TODO, actually do that...
-        stage = previousStageChildren
-        removes = []
-        for current in stage:
-            currentParents = parents.get( current, [] )
-            for parent in currentParents:
-                if parent in stage and parent != current:
-                    # might wind up removing current...
-                    if not current in parents.get(parent, []):
-                        # is not mutually dependent...
-                        removes.append( current )
-        for remove in removes:
-            while remove in stage:
-                stage.remove( remove )
-        stages.append( stage)
-        taken.extend( stage )
-        nodes = filter ( lambda x, l=stage: x not in l, nodes )
-        if nodelen == len(nodes):
-            if noRecursion:
-                raise RecursionError( nodes )
-            else:
-                stages.append( nodes[:] )
-                nodes = []
-    return stages
-
-def _buildChildrenLists (routes):
-    childrenTable = {}
-    parentTable = {}
-    for sourceID,destinationID in routes:
-        currentChildren = childrenTable.get( sourceID, [])
-        currentParents = parentTable.get( destinationID, [])
-        if not destinationID in currentChildren:
-            currentChildren.append ( destinationID)
-        if not sourceID in currentParents:
-            currentParents.append ( sourceID)
-        childrenTable[sourceID] = currentChildren
-        parentTable[destinationID] = currentParents
-    return childrenTable, parentTable
-
-
-def toposort (nodes, routes, noRecursion=1):
-    '''Topological sort from Tim Peters, fairly efficient
-    in comparison (it seems).'''
-    #first calculate the recursion depth
+class Sorter(object):
+    def __init__(self, dependencies):
+        self.dependencies = dependencies
+        self.visited = set()
+        self.sorted = ()
     
-    dependencies = {}
-    inversedependencies = {}
-    if not nodes:
-        return []
-    if not routes:
-        return [nodes]
-    for node in nodes:
-        dependencies[ node ] = (0, node)
-        inversedependencies[ node ] = []
+    def sort(self):
+        for key in self.dependencies:
+            self._visit(key)
+        return self.sorted
     
-    
-    for depended, depends in routes:
-        # is it a null rule
-        try:
-            newdependencylevel, object = dependencies.get ( depends, (0, depends))
-        except TypeError:
-            print depends
-            raise
-        dependencies[ depends ] = (newdependencylevel + 1,  depends)
-        # "dependency (existence) of depended-on"
-        newdependencylevel,object = dependencies.get ( depended, (0, depended) )
-        dependencies[ depended ] = (newdependencylevel, depended)
-        # Inverse dependency set up
-        dependencieslist = inversedependencies.get ( depended, [])
-        dependencieslist.append (depends)
-        inversedependencies[depended] = dependencieslist
-    ### Now we do the actual sorting
-    # The first task is to create the sortable
-    # list of dependency-levels
-    sortinglist = dependencies.values()
-    sortinglist.sort ()
-    output = []
-    while sortinglist:
-        deletelist = []
-        generation = []
-        output.append( generation)
-        while sortinglist and sortinglist[0][0] == 0:
-            number, object = sortinglist[0]
-            generation.append ( object )
-            deletelist.append( object )
-            for inverse in inversedependencies.get(object, () ):
-                try:
-                    oldcount, inverse = dependencies [ inverse]
-                    if oldcount > 0:
-                        # will be dealt with on later pass
-                        dependencies [ inverse] = (oldcount-1, inverse)
-                    else:
-                        # will be dealt with on this pass,
-                        # so needs not to be in the sorting list next time
-                        deletelist.append( inverse )
-                    # just in case a loop comes through
-                    inversedependencies[object] = []
-                except KeyError:
-                    # dealing with a recursion-breaking run...
-                    pass
-            del sortinglist [0]
-        # if no elements could be deleted, then
-        # there is something which depends upon itself
-        if not deletelist:
-            if noRecursion:
-                raise RecursionError( sortinglist )
-            else:
-                # hack so that something gets deleted...
-##                import pdb
-##                pdb.set_trace()
-                dependencies[sortinglist[0][1]] = (0,sortinglist[0][1])
-        # delete the items that were dealt with
-        for item in deletelist:
-            try:
-                del dependencies [ item ]
-            except KeyError:
-                pass
-        # need to recreate the sortinglist
-        sortinglist = dependencies.values()
-        if not generation:
-            output.remove( generation )
-        sortinglist.sort ()
-    return output
+    def _visit(self, key):
+        if key not in self.visited:
+            self.visited.add(key)
+            if not self.dependencies.has_key(key):
+                raise MissingDependency(key)
+            for depends in self.dependencies[key]:
+                self._visit(depends)
+            self.sorted += (key,)
 
-
-
-
-
-if __name__ == "__main__":
-
-    nodes = ['a', 'b', 'c', 'd', 'e', 'f']
-    route = [('a', 'b'), ('b', 'c'), ('b', 'd'), ('e','f')]
-
-    for x in  toposort( nodes, route):
-        for a in x:
-            print a
-
-    raise SystemExit
-
-
-
-    import pprint, traceback
-    nodes= [ 0,1,2,3,4,5 ]
-    testingValues = [
-        [ (0,1),(1,2),(2,3),(3,4),(4,5)],
-        [ (0,1),(0,2),(1,2),(3,4),(4,5)],
-        [
-        (0,1),
-        (0,2),
-        (0,2),
-                    (2,4),
-                    (2,5),
-                (3,2),
-        (0,3)],
-        [
-        (0,1), # 3-element cycle test, no orphan nodes
-        (1,2),
-        (2,0),
-                    (2,4),
-                    (2,5),
-                (3,2),
-        (0,3)],
-        [
-        (0,1),
-        (1,1),
-        (1,1),
-                (1,4),
-                (1,5),
-                (1,2),
-        (3,1),
-        (2,1),
-        (2,0)],
-        [
-            (0,1),
-            (1,0),
-            (0,2),
-            (0,3),
-        ],
-        [
-            (0,1),
-            (1,0),
-            (0,2),
-            (3,1),
-        ],
-    ]
-    print 'sort, no recursion allowed'
-    for index in range(len(testingValues)):
-##        print '    %s -- %s'%( index, testingValues[index])
-        try:
-            print '        ', sort( nodes, testingValues[index] )
-        except:
-            print 'exception raised'
-    print 'toposort, no recursion allowed'
-    for index in range(len(testingValues)):
-##        print '    %s -- %s'%( index, testingValues[index])
-        try:
-            print '        ', toposort( nodes, testingValues[index] )
-        except:
-            print 'exception raised'
-    print 'sort, recursion allowed'
-    for index in range(len(testingValues)):
-##        print '    %s -- %s'%( index, testingValues[index])
-        try:
-            print '        ', sort( nodes, testingValues[index],0 )
-        except:
-            print 'exception raised'
-    print 'toposort, recursion allowed'
-    for index in range(len(testingValues)):
-##        print '    %s -- %s'%( index, testingValues[index])
-        try:
-            print '        ', toposort( nodes, testingValues[index],0 )
-        except:
-            print 'exception raised'
-        
-        
-    
+def toposort(dependencies):
+    """Returns a tuple of the dependencies dictionary keys sorted by entries
+    in the dependency lists.  Given circular dependencies, sort will impose
+    an order.  Raises MissingDependency if a key is not found.
+    """
+    s = Sorter(dependencies)
+    return s.sort()



More information about the Commits mailing list