simplify topological dependency solution

This patch prepares the way for simplifying the topological sort solution for
the classes which implement the document building logic.  Formerly, each
doctype class had to import networx itself and the @depends decorator stuffed
the dependencies into a graph in the class variable.

Now, each method tracks its dependencies (same decorator trick), but the
topological sort is not computed until just before running the job.  This is
more flexible, more obvious, simpler and features less code replication.

The next commit or two will convert the remaining doctype classes to use this
techinque.
This commit is contained in:
Martin A. Brown 2016-03-05 17:04:45 -08:00
parent 7d287b44e5
commit 4c01ae4af7
1 changed files with 22 additions and 11 deletions

View File

@ -8,6 +8,7 @@ import sys
import stat import stat
import errno import errno
import logging import logging
import inspect
from tempfile import NamedTemporaryFile as ntf from tempfile import NamedTemporaryFile as ntf
from functools import wraps from functools import wraps
import networkx as nx import networkx as nx
@ -27,15 +28,13 @@ postamble = '''
''' '''
def depends(graph, *predecessors): def depends(*predecessors):
'''decorator to be used for constructing build order graph''' '''decorator to be used for constructing build order graph'''
def anon(f): def anon(f):
for dep in predecessors:
graph.add_edge(dep.__name__, f.__name__)
@wraps(f) @wraps(f)
def method(self, *args, **kwargs): def method(self, *args, **kwargs):
return f(self, *args, **kwargs) return f(self, *args, **kwargs)
method.depends = [x.__name__ for x in predecessors]
return method return method
return anon return anon
@ -155,19 +154,31 @@ class BaseDoctype(object):
return False return False
return True return True
def determinebuildorder(self):
graph = nx.DiGraph()
d = dict(inspect.getmembers(self, inspect.ismethod))
for name, member in d.items():
predecessors = getattr(member, 'depends', None)
if predecessors:
for pred in predecessors:
method = d.get(pred, None)
assert method is not None
graph.add_edge(method, member)
order = nx.dag.topological_sort(graph)
return order
@logtimings(logger.debug) @logtimings(logger.debug)
def buildall(self): def buildall(self):
stem = self.source.stem stem = self.source.stem
order = nx.dag.topological_sort(self.graph) order = self.determinebuildorder()
logger.debug("%s build order %r", self.source.stem, order) logger.debug("%s build order %r", self.source.stem, order)
for dep in order: for method in order:
method = getattr(self, dep, None)
assert method is not None
classname = self.__class__.__name__ classname = self.__class__.__name__
logger.info("%s calling method %s.%s", stem, classname, dep) logger.info("%s calling method %s.%s",
stem, classname, method.__name__)
if not method(): if not method():
logger.error("%s reported method %s failure, skipping...", logger.error("%s called method %s.%s failed, skipping...",
stem, dep) stem, classname, method.__name__)
return False return False
return True return True