forked from jcorbin/starter_home
-
Notifications
You must be signed in to change notification settings - Fork 0
/
deporder
executable file
·109 lines (89 loc) · 3.09 KB
/
deporder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python
import collections
import os
class Graph(object):
def __init__(self):
self.G = collections.defaultdict(set)
self.H = collections.defaultdict(set)
def update(self, edges):
for a, b in edges:
self.G[a].add(b)
self.H[b].add(a)
def nodes(self):
return set(self.G).union(self.H)
def initial_nodes(self):
return set(self.G).difference(self.H)
def terminal_nodes(self):
return set(self.H).difference(self.G)
def itertopo(self):
G = dict((a, set(bs)) for a, bs in self.G.iteritems())
H = dict((a, set(bs)) for a, bs in self.H.iteritems())
S = set(G).difference(H)
while S:
n = min(S)
S.remove(n)
yield n
for m in G.pop(n, set()):
H[m].remove(n)
if not H[m]:
S.add(m)
cycles = dict((node, out) for node, out in G.iteritems() if out)
if cycles:
raise CycleError(cycles)
class CycleError(ValueError):
def __init__(self, graph):
self.graph = graph
# TODO: cycle extraction
def __str__(self):
return 'cycle involving: ' + repr(self.graph)
def extract_relations(path, name):
with open(path, 'r') as f:
for line in f:
line = line.rstrip('\r\n')
if not line.startswith('#'):
break
fields = line.split(None, 3)
if len(fields) < 3:
continue
rel = fields[1]
other = fields[2]
if rel == 'before:':
yield name, other
elif rel == 'after:':
yield other, name
class DependencyGraph(Graph):
def __init__(self, path):
super(DependencyGraph, self).__init__()
self.path = os.path.expanduser(path)
self.path = os.path.realpath(path)
self.parts = set()
for part, path in self.list_parts():
self.parts.add(part)
self.update(extract_relations(path, part))
self.update((node, '.explicit') for node in self.terminal_nodes())
self.update(('.explicit', node) for node in self.unspecified_parts())
def list_parts(self):
for part in os.listdir(self.path):
if part.startswith('.'):
continue
path = os.path.join(self.path, part)
if not os.path.isfile(path):
continue
yield part, path
def unspecified_parts(self):
return self.parts.difference(self.nodes())
def main(argv=None):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--dump', action='store_true')
parser.add_argument('-f', '--full', action='store_true')
parser.add_argument('path', default='.', nargs='?')
args = parser.parse_args(args=argv)
G = DependencyGraph(args.path)
parts = [node for node in G.itertopo() if node in G.parts]
if args.full:
parts = [os.path.join(G.path, part) for part in parts]
for part in parts:
print part
if __name__ == '__main__':
main()