def boil(self, external_nodes): """ Resolve everything we got, propagate build information backwards across the edges. external_nodes is an iterable of repo nodes. """ loop_count = 0 while True: loop_count += 1 if loop_count > 1000: raise self.InfiniteLoopError() something_changed = self.__do_enlarge() if something_changed: continue do_next_round = False directory_builders = [] for b in self.iter_builders(): if not isinstance(b, DirectoryBuilder): continue directory_builders.append(b) b.recollect_dependency_info() if b.node_dependency_info_changed(): do_next_round = True pass pass if do_next_round: self.__current_digraph = None continue if self.__current_digraph: break all_nodes = directory_builders + list(external_nodes) self.__current_digraph = DirectedGraph( nodes=all_nodes, edgefinder=EdgeFinder(nodes=all_nodes)) for n in directory_builders: n.node_relate_managed_builders(digraph=self.__current_digraph) pass pass self.__current_digraph.edgefinder().raise_unresolved() pass
def test1(self): # bad1 # / \ # root / good yields good # \ /---- # \ / # bad2 root = Node(Node.BAD) bad1 = Node(Node.BAD) bad2 = Node(Node.BAD) good = Node(Node.GOOD) digraph = DirectedGraph(nodes=[root, bad1, bad2, good], edges=[ Edge(tail=root, head=bad1), Edge(tail=root, head=bad2), Edge(tail=bad1, head=good), Edge(tail=bad2, head=good) ]) self.failUnlessEqual( set([good]), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
def test7(self): # bad # / # / # / # root ----------- good1 # \ ----- yields good1, good2 (good1 is seen through good2, # \ / but is a direct successor of root) # \ / # good2 # ----- root = Node(Node.BAD) bad = Node(Node.BAD) good1 = Node(Node.GOOD) good2 = Node(Node.GOOD) digraph = DirectedGraph(nodes=[root, bad, good1, good2], edges=[ Edge(root, bad), Edge(root, good1), Edge(root, good2), Edge(good2, good1) ]) self.failUnlessEqual( set([good1, good2]), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
def test6(self): # bad # / \ # / \ # root good2 # \ ----- yields good1, good2 # \ # good1 # ----- root = Node(Node.BAD) bad = Node(Node.BAD) good1 = Node(Node.GOOD) good2 = Node(Node.GOOD) digraph = DirectedGraph( nodes=[root, bad, good1, good2], edges=[Edge(root, bad), Edge(root, good1), Edge(bad, good2)]) self.failUnlessEqual( set([good1, good2]), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
def test3(self): # good1 --- good3 # / ----- ----- # bad1 / # / \ yields good1, good2 # root / \ good2 # \ /----- # \ / # bad2 / root = Node(Node.BAD) bad1 = Node(Node.BAD) bad2 = Node(Node.BAD) good1 = Node(Node.GOOD) good2 = Node(Node.GOOD) good3 = Node(Node.GOOD) digraph = DirectedGraph(nodes=[root, bad1, bad2, good1, good2, good3], edges=[ Edge(root, bad1), Edge(root, bad2), Edge(bad1, good1), Edge(bad1, good2), Edge(bad2, good2), Edge(good1, good3) ]) self.failUnlessEqual( set([good1, good2]), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
def test0(self): # root ---- bad yields [] root = Node(Node.BAD) bad = Node(Node.BAD) digraph = DirectedGraph(nodes=[root, bad], edges=[Edge(root, bad)]) self.failUnlessEqual( set(), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
def test(self): n1 = object() n2 = object() n3 = object() n4 = object() g = algorithm.combine_graphs([DirectedGraph(nodes=[n1, n2, n3], edges=[Edge(n1, n2), Edge(n1, n3)]), DirectedGraph(nodes=[n2, n3], edges=[Edge(n2, n3)]), DirectedGraph(nodes=[n3, n4], edges=[Edge(n3, n4)]), DirectedGraph(nodes=[n1, n2], edges=[Edge(n1, n2)])]) self.failUnlessEqual(set([n1, n2, n3, n4]), set(g.nodes())) self.failUnless(g.find_edge(n1, n2)) self.failUnless(g.find_edge(n1, n3)) self.failUnless(g.find_edge(n2, n3)) self.failUnless(g.find_edge(n3, n4)) pass
def test(self): n1 = object() n2 = object() n3 = object() digraph = DirectedGraph(nodes=[n1, n2, n3], edges=[Edge(n1, n2), Edge(n1, n3)]) self.failUnlessEqual(set([n1, n2, n3]), set(algorithm.nodes_reached_from_including_entry(digraph, entrypoints=[n1]))) digraph = DirectedGraph(nodes=[n1, n2, n3], edges=[Edge(n1, n2), Edge(n2, n3)]) self.failUnlessEqual(set([n1, n2, n3]), set(algorithm.nodes_reached_from_including_entry(digraph, entrypoints=[n1]))) digraph = DirectedGraph(nodes=[n1, n2, n3], edges=[Edge(n1, n2), Edge(n2, n3)]) self.failUnlessEqual(set([n2, n3]), set(algorithm.nodes_reached_from_including_entry(digraph, entrypoints=[n2]))) pass
def main(): try: nodes = [] for filename in sys.argv[1:]: pkg = PackageFile(File(lines=lines_of_file(filename))).load() nodes.extend(pkg.nodes()) pass graph = DirectedGraph(nodes=nodes, edgefinder=EdgeFinder(nodes=nodes)) print '\n'.join(write_graph(graph)) except Error, e: print ` e `
def test8(self): # root --- good1 --- good2 yields good1 root = Node(Node.BAD) good1 = Node(Node.GOOD) good2 = Node(Node.GOOD) digraph = DirectedGraph(nodes=[root, good1, good2], edges=[Edge(root, good1), Edge(good1, good2)]) self.failUnlessEqual( set([good1]), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
def test(self): n1 = object() n2 = object() n3 = object() n4 = object() digraph = DirectedGraph(nodes=[n1, n2, n3, n4], edges=[Edge(n1, n2), Edge(n2, n3), Edge(n3, n4), Edge(n1, n4)]) subtracted_digraph = algorithm.subtract_nodes(digraph=digraph, nodes=[n2, n3]) self.failUnlessEqual(set([n1, n4]), set(subtracted_digraph.nodes())) self.failUnless(subtracted_digraph.find_edge(n1, n4)) pass
def test2(self): # bad # / \ # root /______\ good yields good # ---- root = Node(Node.BAD) bad = Node(Node.BAD) good = Node(Node.GOOD) digraph = DirectedGraph( nodes=[root, bad, good], edges=[Edge(root, bad), Edge(root, good), Edge(bad, good)]) self.failUnlessEqual( set([good]), algorithm.nearest_property(digraph=digraph, entrypoint=root, property=GoodProperty())) pass
class LocalPackage(Package): class InfiniteLoopError(Error): def __init__(self): Error.__init__( self, 'Enlarge-loop entered for a ridiculously large number of times ' '(some Builder must be misbehaving)') pass pass def __init__(self, rootdirectory, setups): self.__name = None self.__version = None self.__rootdirectory = rootdirectory self.__setup = None if setups: self.__set_setup(setups) pass self.__current_digraph = None # our directory builders, sorted topologically for output by # our backends. self.__topo_directories = None # read package definition file pkgdeffile = self.__rootdirectory.find([const.CONFIX2_PKG]) if pkgdeffile is None: raise Error(const.CONFIX2_PKG + ' missing in ' + os.sep.join(self.__rootdirectory.abspath())) InterfaceExecutor(iface_pieces=[PackageInterfaceProxy( package=self)]).execute_file(pkgdeffile) if self.__name is None: raise Error(const.CONFIX2_PKG + ': package name has not been set') if self.__version is None: raise Error(const.CONFIX2_PKG + ': package version has not been set') if self.__setup is None: raise Error('No setup has been given; use SETUP() in Confix2.pkg') # create our root builder self.__rootbuilder = DirectoryBuilder(directory=rootdirectory) # now's the time to make everyone aware that we're no fun # anymore. self.__rootbuilder.initialize(package=self) pass def __str__(self): return 'LocalPackage:' + str(self.__name) def name(self): return self.__name def set_name(self, name): assert self.__name is None self.__name = name pass def version(self): return self.__version def set_version(self, version): assert self.__version is None self.__version = version pass def rootdirectory(self): return self.__rootdirectory def setup(self): return self.__setup def set_setup(self, s): self.__set_setup(s) pass def rootbuilder(self): return self.__rootbuilder def topo_directories(self): """ Returns a toplogically sorted list of directory builder objects. Valid only during the output phase, once dependency calculation is complete. """ assert self.__topo_directories is not None return self.__topo_directories def repofilename(self): """ The name of the package's repo file. """ return self.name() + '.repo' def digraph(self): return self.__current_digraph def boil(self, external_nodes): """ Resolve everything we got, propagate build information backwards across the edges. external_nodes is an iterable of repo nodes. """ loop_count = 0 while True: loop_count += 1 if loop_count > 1000: raise self.InfiniteLoopError() something_changed = self.__do_enlarge() if something_changed: continue do_next_round = False directory_builders = [] for b in self.iter_builders(): if not isinstance(b, DirectoryBuilder): continue directory_builders.append(b) b.recollect_dependency_info() if b.node_dependency_info_changed(): do_next_round = True pass pass if do_next_round: self.__current_digraph = None continue if self.__current_digraph: break all_nodes = directory_builders + list(external_nodes) self.__current_digraph = DirectedGraph( nodes=all_nodes, edgefinder=EdgeFinder(nodes=all_nodes)) for n in directory_builders: n.node_relate_managed_builders(digraph=self.__current_digraph) pass pass self.__current_digraph.edgefinder().raise_unresolved() pass def output(self): self.__topo_directories = self.__sort_subdirs() # generate the package's repo file. repofilename = self.repofilename() repofile = self.__rootdirectory.find([repofilename]) if repofile is None: repofile = self.__rootdirectory.add(name=repofilename, entry=File()) else: repofile.truncate() pass PackageFile(file=repofile).dump(package=self.install()) # recursively write the package's output self.__rootbuilder.output() pass def install(self): installed_nodes = [] for b in self.iter_builders(): if isinstance(b, DirectoryBuilder): installed_nodes.append(b.install()) pass pass return InstalledPackage(name=self.name(), version=self.version(), nodes=installed_nodes) def iter_builders(self): """ Returns an iterator over all builder objects that are maintained by this package. Use it when you do not intend to modify the set of builders when iterating. """ yield self.__rootbuilder for builder in self.__rootbuilder.iter_builders_recursive(): yield builder pass pass def __do_enlarge(self): """ Enlarge our current set of builders by calling Builder.enlarge() on each. @return True if something has changed, False otherwise. """ # copy them out because we will be changing the set once we # call enlarge on each builder. builders = [b for b in self.iter_builders()] prev_builder_props = {} for b in builders: prev_builder_props[b] = b.force_enlarge_count() pass for b in builders: try: b.enlarge() except Error as e: raise Error( "Builder " + b.shortname() + " of " + b.parentbuilder().shortname() + " encountered an error", [e]) pass for b in self.iter_builders(): prev_enlarge_count = prev_builder_props.get(b) if prev_enlarge_count is None: # this is a new builder return True if prev_enlarge_count < b.force_enlarge_count(): # b forced repetition return True pass return False def __sort_subdirs(self): # sort subdirectories topologically for our backends. subdir_nodes = set() for b in self.iter_builders(): if isinstance(b, DirectoryBuilder): subdir_nodes.add(b) pass pass graph = algorithm.subgraph(digraph=self.__current_digraph, nodes=subdir_nodes) return toposort.toposort(digraph=graph, nodes=subdir_nodes) def __set_setup(self, s): assert self.__setup is None if isinstance(s, Setup): self.__setup = s elif type(s) in (list, tuple): self.__setup = CompositeSetup(s) else: assert False pass pass pass