forked from kivy/python-for-android
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdepsort.py
executable file
·199 lines (157 loc) · 5.3 KB
/
depsort.py
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#! /usr/bin/env python2
import argparse
import sys
class Graph(object):
def __init__(self):
# `graph`: dict that maps each package to a set of its dependencies.
self.graph = {}
def add(self, dependent, dependency):
"""Add a dependency relationship to the graph"""
self.graph.setdefault(dependent, set())
self.graph.setdefault(dependency, set())
if dependent != dependency:
self.graph[dependent].add(dependency)
def add_optional(self, dependent, dependency):
"""Add an optional (ordering only) dependency relationship to the graph
Only call this after all mandatory requirements are added
"""
if dependent in self.graph and dependency in self.graph:
self.add(dependent, dependency)
def find_order(self):
"""Do a topological sort on a dependency graph
:Parameters:
:Returns:
iterator, sorted items form first to last
"""
graph = dict((k, set(v)) for k, v in self.graph.items())
while graph:
# Find all items without a parent
leftmost = [l for l, s in graph.items() if not s]
if not leftmost:
raise ValueError('Dependency cycle detected! %s' % graph)
# If there is more than one, sort them for predictable order
leftmost.sort()
for result in leftmost:
# Yield and remove them from the graph
yield result
graph.pop(result)
for bset in graph.values():
bset.discard(result)
def lines_to_relationships(lines):
"""Do a topological sort from a list of space-separated lines
:Parameters:
`lines`: Iterable of lines in the format
"dependent dependency_0 dependency_1 ... dependency_n"
:Returns:
iterator of (dependent, dependency) tuples
"""
for line in lines:
line = line.split()
if line:
dependent = line[0]
for dependency in line:
yield dependent, dependency
def topo_sort_lines(lines, lines_optional=()):
"""Do a topological sort from a list of space-separated lines
:Parameters:
`lines`: Iterable of lines in the format
"dependent dependency_0 dependency_1 ... dependency_n"
`lines`: Iterable of lines with *optional* (ordering-only) dependencies
"dependent dependency_0 dependency_1 ... dependency_n"
:Returns:
string, Sorted dependencies, space-separated
"""
graph = Graph()
for dependent, dependency in lines_to_relationships(lines):
graph.add(dependent, dependency)
for dependent, dependency in lines_to_relationships(lines_optional):
graph.add_optional(dependent, dependency)
return ' '.join(graph.find_order())
def test_depsort_1():
lines = [
'c a',
'b c',
'd b',
'w z',
'a w',
]
assert topo_sort_lines(lines) == 'z w a c b d'
def test_depsort_2():
lines = [
'l k',
'm l',
'a k',
'd a',
'l d',
's l',
'm s',
]
assert topo_sort_lines(lines) == 'k a d l s m'
def test_depsort_3():
lines = [
'z a',
's z',
'z z',
's s',
]
assert topo_sort_lines(lines) == 'a z s'
def test_depsort_4():
lines = [
'f d',
'g f',
'r f',
't r',
'y t',
'g y',
]
assert topo_sort_lines(lines) == 'd f r t y g'
def test_depsort_5():
lines = [
'a b c d e f',
'e f',
'g',
]
assert topo_sort_lines(lines) == 'b c d f g e a'
def test_depsort_6():
lines = [
' numpy python ',
' kivy pygame pyjnius android ',
' python hostpython ',
' pygame sdl ',
' android pygame ',
' pyjnius python ',
' sdl python ',
' hostpython ',
]
assert topo_sort_lines(lines) == (
'hostpython python numpy pyjnius sdl pygame android kivy')
def test_depsort_optional_1():
lines = [' myapp openssl python ']
optional = ['python openssl']
assert topo_sort_lines(lines, optional) == 'openssl python myapp'
def test_depsort_optional_2():
lines = [' myapp openssl python ']
# Just for testing purposes, make openssl soft-depend on python
optional = ['openssl python']
assert topo_sort_lines(lines, optional) == 'python openssl myapp'
def test_depsort_optional_3():
lines = ['myapp openssl']
optional = ['python openssl']
assert topo_sort_lines(lines, optional) == 'openssl myapp'
def test_depsort_optional_4():
lines = ['myapp python']
optional = ['python openssl']
assert topo_sort_lines(lines, optional) == 'python myapp'
def test_depsort_optional_4():
lines = ['myapp']
optional = ['python openssl']
assert topo_sort_lines(lines, optional) == 'myapp'
def main(argv):
parser = argparse.ArgumentParser(
description="Sort dependencies given on standard input")
parser.add_argument('--optional', type=argparse.FileType('r'),
help='File with optional (ordering-only) dependencies')
args = parser.parse_args(argv[1:])
return topo_sort_lines(sys.stdin, lines_optional=args.optional or [])
if __name__ == '__main__':
print main(sys.argv)