Arranging a dependency tree

Heiko Wundram heikowu at ceosg.de
Wed Aug 11 23:13:36 EDT 2004


Basically, dependencies (should) create a tree structure, except when you have 
circular dependencies. Now, what you have to do to resolve the tree structure 
in case there are no circular dependencies is to walk the edges of the graph, 
from leaf to top.

Example graph:

x
| ----> y
| ----> z | ----> a
|            | ----> b
| ----> a

Now, you need a way to store this in Python, I'd use dictionaries:

deps = {"x":
    {"y":{},
     "z":{"a":{}}
    }}

Reading the data into a format like this is pretty simple too:

<code>
def makeTree(files):
	"""Pass in a list of files for which dependency info
	should be generated."""

	deps = {}
	cur_files = [(fname,deps) for fname in files]
	all_deps = {}
	while cur_files:
		cur_file, cur_deps = cur_files.pop()
		if cur_file in all_deps:
			cur_deps[cur_file] = all_deps[cur_file]
			continue
		new_deps = {}
		# getDeps returns the dependencies needed by cur_file.
		for dep in getDeps(cur_file):
			cur_files.append((dep,new_deps))
		cur_deps[cur_file] = new_deps
		all_deps[cur_file] = new_deps
	return deps
</code>

Now, reading in the dependency list as done above properly takes care of 
circular dependencies, and might create dictionaries which contain themselves 
at some depth. It won't read dependency info more than once for a given file, 
too.

The following code will walk the tree generated by makeTree, but it will barf 
on circular dependencies. You'll have to devise some means to deal with 
these, the most common form being simply to break tree walking at the current 
level in case you encounter a circular dependency. The algorithm is 
recursive. Doing it iteratively would be possible too, but at quite some 
cost.

<code>
def _walkTree(tree,cur_files,walked_files):
	for cur_file, cur_deps in tree.iteritems():
		if cur_file in walked_files:
			continue
		elif cur_file in cur_files:
			# Deal with circular deps here. You might just do:
			# raise StopIteration
			raise ValueError, "Circular tree structure."
		if cur_deps:
			for sub_file in _walkTree(cur_deps,cur_files+(cur_file,),walked_files):
				yield sub_file
		walked_files.append(cur_file)
		yield cur_file

def walkTree(tree):
	"""Walk a tree as generated by makeTree."""

	for f in _walkTree(tree,(),[]):
		yield f
</code>

Well, hope this helps and gives you some hints!

Heiko.



More information about the Python-list mailing list