1 |
Author: zmedico |
2 |
Date: 2010-02-22 02:50:49 +0000 (Mon, 22 Feb 2010) |
3 |
New Revision: 15423 |
4 |
|
5 |
Added: |
6 |
main/trunk/pym/portage/util/digraph.py |
7 |
Modified: |
8 |
main/trunk/pym/portage/__init__.py |
9 |
Log: |
10 |
Move portage.digraph class to portage.util.digraph.digraph. |
11 |
|
12 |
|
13 |
Modified: main/trunk/pym/portage/__init__.py |
14 |
=================================================================== |
15 |
--- main/trunk/pym/portage/__init__.py 2010-02-22 02:39:48 UTC (rev 15422) |
16 |
+++ main/trunk/pym/portage/__init__.py 2010-02-22 02:50:49 UTC (rev 15423) |
17 |
@@ -115,6 +115,7 @@ |
18 |
'pickle_read,pickle_write,stack_dictlist,stack_dicts,' + \ |
19 |
'stack_lists,unique_array,varexpand,writedict,writemsg,' + \ |
20 |
'writemsg_stdout,write_atomic', |
21 |
+ 'portage.util.digraph:digraph', |
22 |
'portage.versions', |
23 |
'portage.versions:best,catpkgsplit,catsplit,cpv_getkey,' + \ |
24 |
'cpv_getkey@getCPFromCPV,endversion_keys,' + \ |
25 |
@@ -679,282 +680,6 @@ |
26 |
|
27 |
return rlist |
28 |
|
29 |
-#beautiful directed graph object |
30 |
- |
31 |
-class digraph(object): |
32 |
- def __init__(self): |
33 |
- """Create an empty digraph""" |
34 |
- |
35 |
- # { node : ( { child : priority } , { parent : priority } ) } |
36 |
- self.nodes = {} |
37 |
- self.order = [] |
38 |
- |
39 |
- def add(self, node, parent, priority=0): |
40 |
- """Adds the specified node with the specified parent. |
41 |
- |
42 |
- If the dep is a soft-dep and the node already has a hard |
43 |
- relationship to the parent, the relationship is left as hard.""" |
44 |
- |
45 |
- if node not in self.nodes: |
46 |
- self.nodes[node] = ({}, {}, node) |
47 |
- self.order.append(node) |
48 |
- |
49 |
- if not parent: |
50 |
- return |
51 |
- |
52 |
- if parent not in self.nodes: |
53 |
- self.nodes[parent] = ({}, {}, parent) |
54 |
- self.order.append(parent) |
55 |
- |
56 |
- priorities = self.nodes[node][1].get(parent) |
57 |
- if priorities is None: |
58 |
- priorities = [] |
59 |
- self.nodes[node][1][parent] = priorities |
60 |
- self.nodes[parent][0][node] = priorities |
61 |
- priorities.append(priority) |
62 |
- priorities.sort() |
63 |
- |
64 |
- def remove(self, node): |
65 |
- """Removes the specified node from the digraph, also removing |
66 |
- and ties to other nodes in the digraph. Raises KeyError if the |
67 |
- node doesn't exist.""" |
68 |
- |
69 |
- if node not in self.nodes: |
70 |
- raise KeyError(node) |
71 |
- |
72 |
- for parent in self.nodes[node][1]: |
73 |
- del self.nodes[parent][0][node] |
74 |
- for child in self.nodes[node][0]: |
75 |
- del self.nodes[child][1][node] |
76 |
- |
77 |
- del self.nodes[node] |
78 |
- self.order.remove(node) |
79 |
- |
80 |
- def difference_update(self, t): |
81 |
- """ |
82 |
- Remove all given nodes from node_set. This is more efficient |
83 |
- than multiple calls to the remove() method. |
84 |
- """ |
85 |
- if isinstance(t, (list, tuple)) or \ |
86 |
- not hasattr(t, "__contains__"): |
87 |
- t = frozenset(t) |
88 |
- order = [] |
89 |
- for node in self.order: |
90 |
- if node not in t: |
91 |
- order.append(node) |
92 |
- continue |
93 |
- for parent in self.nodes[node][1]: |
94 |
- del self.nodes[parent][0][node] |
95 |
- for child in self.nodes[node][0]: |
96 |
- del self.nodes[child][1][node] |
97 |
- del self.nodes[node] |
98 |
- self.order = order |
99 |
- |
100 |
- def remove_edge(self, child, parent): |
101 |
- """ |
102 |
- Remove edge in the direction from child to parent. Note that it is |
103 |
- possible for a remaining edge to exist in the opposite direction. |
104 |
- Any endpoint vertices that become isolated will remain in the graph. |
105 |
- """ |
106 |
- |
107 |
- # Nothing should be modified when a KeyError is raised. |
108 |
- for k in parent, child: |
109 |
- if k not in self.nodes: |
110 |
- raise KeyError(k) |
111 |
- |
112 |
- # Make sure the edge exists. |
113 |
- if child not in self.nodes[parent][0]: |
114 |
- raise KeyError(child) |
115 |
- if parent not in self.nodes[child][1]: |
116 |
- raise KeyError(parent) |
117 |
- |
118 |
- # Remove the edge. |
119 |
- del self.nodes[child][1][parent] |
120 |
- del self.nodes[parent][0][child] |
121 |
- |
122 |
- def __iter__(self): |
123 |
- return iter(self.order) |
124 |
- |
125 |
- def contains(self, node): |
126 |
- """Checks if the digraph contains mynode""" |
127 |
- return node in self.nodes |
128 |
- |
129 |
- def get(self, key, default=None): |
130 |
- node_data = self.nodes.get(key, self) |
131 |
- if node_data is self: |
132 |
- return default |
133 |
- return node_data[2] |
134 |
- |
135 |
- def all_nodes(self): |
136 |
- """Return a list of all nodes in the graph""" |
137 |
- return self.order[:] |
138 |
- |
139 |
- def child_nodes(self, node, ignore_priority=None): |
140 |
- """Return all children of the specified node""" |
141 |
- if ignore_priority is None: |
142 |
- return list(self.nodes[node][0]) |
143 |
- children = [] |
144 |
- if hasattr(ignore_priority, '__call__'): |
145 |
- for child, priorities in self.nodes[node][0].items(): |
146 |
- for priority in priorities: |
147 |
- if not ignore_priority(priority): |
148 |
- children.append(child) |
149 |
- break |
150 |
- else: |
151 |
- for child, priorities in self.nodes[node][0].items(): |
152 |
- if ignore_priority < priorities[-1]: |
153 |
- children.append(child) |
154 |
- return children |
155 |
- |
156 |
- def parent_nodes(self, node, ignore_priority=None): |
157 |
- """Return all parents of the specified node""" |
158 |
- if ignore_priority is None: |
159 |
- return list(self.nodes[node][1]) |
160 |
- parents = [] |
161 |
- if hasattr(ignore_priority, '__call__'): |
162 |
- for parent, priorities in self.nodes[node][1].items(): |
163 |
- for priority in priorities: |
164 |
- if not ignore_priority(priority): |
165 |
- parents.append(parent) |
166 |
- break |
167 |
- else: |
168 |
- for parent, priorities in self.nodes[node][1].items(): |
169 |
- if ignore_priority < priorities[-1]: |
170 |
- parents.append(parent) |
171 |
- return parents |
172 |
- |
173 |
- def leaf_nodes(self, ignore_priority=None): |
174 |
- """Return all nodes that have no children |
175 |
- |
176 |
- If ignore_soft_deps is True, soft deps are not counted as |
177 |
- children in calculations.""" |
178 |
- |
179 |
- leaf_nodes = [] |
180 |
- if ignore_priority is None: |
181 |
- for node in self.order: |
182 |
- if not self.nodes[node][0]: |
183 |
- leaf_nodes.append(node) |
184 |
- elif hasattr(ignore_priority, '__call__'): |
185 |
- for node in self.order: |
186 |
- is_leaf_node = True |
187 |
- for child, priorities in self.nodes[node][0].items(): |
188 |
- for priority in priorities: |
189 |
- if not ignore_priority(priority): |
190 |
- is_leaf_node = False |
191 |
- break |
192 |
- if not is_leaf_node: |
193 |
- break |
194 |
- if is_leaf_node: |
195 |
- leaf_nodes.append(node) |
196 |
- else: |
197 |
- for node in self.order: |
198 |
- is_leaf_node = True |
199 |
- for child, priorities in self.nodes[node][0].items(): |
200 |
- if ignore_priority < priorities[-1]: |
201 |
- is_leaf_node = False |
202 |
- break |
203 |
- if is_leaf_node: |
204 |
- leaf_nodes.append(node) |
205 |
- return leaf_nodes |
206 |
- |
207 |
- def root_nodes(self, ignore_priority=None): |
208 |
- """Return all nodes that have no parents. |
209 |
- |
210 |
- If ignore_soft_deps is True, soft deps are not counted as |
211 |
- parents in calculations.""" |
212 |
- |
213 |
- root_nodes = [] |
214 |
- if ignore_priority is None: |
215 |
- for node in self.order: |
216 |
- if not self.nodes[node][1]: |
217 |
- root_nodes.append(node) |
218 |
- elif hasattr(ignore_priority, '__call__'): |
219 |
- for node in self.order: |
220 |
- is_root_node = True |
221 |
- for parent, priorities in self.nodes[node][1].items(): |
222 |
- for priority in priorities: |
223 |
- if not ignore_priority(priority): |
224 |
- is_root_node = False |
225 |
- break |
226 |
- if not is_root_node: |
227 |
- break |
228 |
- if is_root_node: |
229 |
- root_nodes.append(node) |
230 |
- else: |
231 |
- for node in self.order: |
232 |
- is_root_node = True |
233 |
- for parent, priorities in self.nodes[node][1].items(): |
234 |
- if ignore_priority < priorities[-1]: |
235 |
- is_root_node = False |
236 |
- break |
237 |
- if is_root_node: |
238 |
- root_nodes.append(node) |
239 |
- return root_nodes |
240 |
- |
241 |
- def is_empty(self): |
242 |
- """Checks if the digraph is empty""" |
243 |
- return len(self.nodes) == 0 |
244 |
- |
245 |
- def clone(self): |
246 |
- clone = digraph() |
247 |
- clone.nodes = {} |
248 |
- memo = {} |
249 |
- for children, parents, node in self.nodes.values(): |
250 |
- children_clone = {} |
251 |
- for child, priorities in children.items(): |
252 |
- priorities_clone = memo.get(id(priorities)) |
253 |
- if priorities_clone is None: |
254 |
- priorities_clone = priorities[:] |
255 |
- memo[id(priorities)] = priorities_clone |
256 |
- children_clone[child] = priorities_clone |
257 |
- parents_clone = {} |
258 |
- for parent, priorities in parents.items(): |
259 |
- priorities_clone = memo.get(id(priorities)) |
260 |
- if priorities_clone is None: |
261 |
- priorities_clone = priorities[:] |
262 |
- memo[id(priorities)] = priorities_clone |
263 |
- parents_clone[parent] = priorities_clone |
264 |
- clone.nodes[node] = (children_clone, parents_clone, node) |
265 |
- clone.order = self.order[:] |
266 |
- return clone |
267 |
- |
268 |
- # Backward compatibility |
269 |
- addnode = add |
270 |
- allnodes = all_nodes |
271 |
- allzeros = leaf_nodes |
272 |
- hasnode = contains |
273 |
- __contains__ = contains |
274 |
- empty = is_empty |
275 |
- copy = clone |
276 |
- |
277 |
- def delnode(self, node): |
278 |
- try: |
279 |
- self.remove(node) |
280 |
- except KeyError: |
281 |
- pass |
282 |
- |
283 |
- def firstzero(self): |
284 |
- leaf_nodes = self.leaf_nodes() |
285 |
- if leaf_nodes: |
286 |
- return leaf_nodes[0] |
287 |
- return None |
288 |
- |
289 |
- def hasallzeros(self, ignore_priority=None): |
290 |
- return len(self.leaf_nodes(ignore_priority=ignore_priority)) == \ |
291 |
- len(self.order) |
292 |
- |
293 |
- def debug_print(self): |
294 |
- def output(s): |
295 |
- writemsg(s, noiselevel=-1) |
296 |
- for node in self.nodes: |
297 |
- output("%s " % (node,)) |
298 |
- if self.nodes[node][0]: |
299 |
- output("depends on\n") |
300 |
- else: |
301 |
- output("(no children)\n") |
302 |
- for child, priorities in self.nodes[node][0].items(): |
303 |
- output(" %s (%s)\n" % (child, priorities[-1],)) |
304 |
- |
305 |
#parse /etc/env.d and generate /etc/profile.env |
306 |
|
307 |
def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None, |
308 |
|
309 |
Added: main/trunk/pym/portage/util/digraph.py |
310 |
=================================================================== |
311 |
--- main/trunk/pym/portage/util/digraph.py (rev 0) |
312 |
+++ main/trunk/pym/portage/util/digraph.py 2010-02-22 02:50:49 UTC (rev 15423) |
313 |
@@ -0,0 +1,281 @@ |
314 |
+# Copyright 2010 Gentoo Foundation |
315 |
+# Distributed under the terms of the GNU General Public License v2 |
316 |
+# $Id$ |
317 |
+ |
318 |
+class digraph(object): |
319 |
+ """ |
320 |
+ A directed graph object. |
321 |
+ """ |
322 |
+ |
323 |
+ def __init__(self): |
324 |
+ """Create an empty digraph""" |
325 |
+ |
326 |
+ # { node : ( { child : priority } , { parent : priority } ) } |
327 |
+ self.nodes = {} |
328 |
+ self.order = [] |
329 |
+ |
330 |
+ def add(self, node, parent, priority=0): |
331 |
+ """Adds the specified node with the specified parent. |
332 |
+ |
333 |
+ If the dep is a soft-dep and the node already has a hard |
334 |
+ relationship to the parent, the relationship is left as hard.""" |
335 |
+ |
336 |
+ if node not in self.nodes: |
337 |
+ self.nodes[node] = ({}, {}, node) |
338 |
+ self.order.append(node) |
339 |
+ |
340 |
+ if not parent: |
341 |
+ return |
342 |
+ |
343 |
+ if parent not in self.nodes: |
344 |
+ self.nodes[parent] = ({}, {}, parent) |
345 |
+ self.order.append(parent) |
346 |
+ |
347 |
+ priorities = self.nodes[node][1].get(parent) |
348 |
+ if priorities is None: |
349 |
+ priorities = [] |
350 |
+ self.nodes[node][1][parent] = priorities |
351 |
+ self.nodes[parent][0][node] = priorities |
352 |
+ priorities.append(priority) |
353 |
+ priorities.sort() |
354 |
+ |
355 |
+ def remove(self, node): |
356 |
+ """Removes the specified node from the digraph, also removing |
357 |
+ and ties to other nodes in the digraph. Raises KeyError if the |
358 |
+ node doesn't exist.""" |
359 |
+ |
360 |
+ if node not in self.nodes: |
361 |
+ raise KeyError(node) |
362 |
+ |
363 |
+ for parent in self.nodes[node][1]: |
364 |
+ del self.nodes[parent][0][node] |
365 |
+ for child in self.nodes[node][0]: |
366 |
+ del self.nodes[child][1][node] |
367 |
+ |
368 |
+ del self.nodes[node] |
369 |
+ self.order.remove(node) |
370 |
+ |
371 |
+ def difference_update(self, t): |
372 |
+ """ |
373 |
+ Remove all given nodes from node_set. This is more efficient |
374 |
+ than multiple calls to the remove() method. |
375 |
+ """ |
376 |
+ if isinstance(t, (list, tuple)) or \ |
377 |
+ not hasattr(t, "__contains__"): |
378 |
+ t = frozenset(t) |
379 |
+ order = [] |
380 |
+ for node in self.order: |
381 |
+ if node not in t: |
382 |
+ order.append(node) |
383 |
+ continue |
384 |
+ for parent in self.nodes[node][1]: |
385 |
+ del self.nodes[parent][0][node] |
386 |
+ for child in self.nodes[node][0]: |
387 |
+ del self.nodes[child][1][node] |
388 |
+ del self.nodes[node] |
389 |
+ self.order = order |
390 |
+ |
391 |
+ def remove_edge(self, child, parent): |
392 |
+ """ |
393 |
+ Remove edge in the direction from child to parent. Note that it is |
394 |
+ possible for a remaining edge to exist in the opposite direction. |
395 |
+ Any endpoint vertices that become isolated will remain in the graph. |
396 |
+ """ |
397 |
+ |
398 |
+ # Nothing should be modified when a KeyError is raised. |
399 |
+ for k in parent, child: |
400 |
+ if k not in self.nodes: |
401 |
+ raise KeyError(k) |
402 |
+ |
403 |
+ # Make sure the edge exists. |
404 |
+ if child not in self.nodes[parent][0]: |
405 |
+ raise KeyError(child) |
406 |
+ if parent not in self.nodes[child][1]: |
407 |
+ raise KeyError(parent) |
408 |
+ |
409 |
+ # Remove the edge. |
410 |
+ del self.nodes[child][1][parent] |
411 |
+ del self.nodes[parent][0][child] |
412 |
+ |
413 |
+ def __iter__(self): |
414 |
+ return iter(self.order) |
415 |
+ |
416 |
+ def contains(self, node): |
417 |
+ """Checks if the digraph contains mynode""" |
418 |
+ return node in self.nodes |
419 |
+ |
420 |
+ def get(self, key, default=None): |
421 |
+ node_data = self.nodes.get(key, self) |
422 |
+ if node_data is self: |
423 |
+ return default |
424 |
+ return node_data[2] |
425 |
+ |
426 |
+ def all_nodes(self): |
427 |
+ """Return a list of all nodes in the graph""" |
428 |
+ return self.order[:] |
429 |
+ |
430 |
+ def child_nodes(self, node, ignore_priority=None): |
431 |
+ """Return all children of the specified node""" |
432 |
+ if ignore_priority is None: |
433 |
+ return list(self.nodes[node][0]) |
434 |
+ children = [] |
435 |
+ if hasattr(ignore_priority, '__call__'): |
436 |
+ for child, priorities in self.nodes[node][0].items(): |
437 |
+ for priority in priorities: |
438 |
+ if not ignore_priority(priority): |
439 |
+ children.append(child) |
440 |
+ break |
441 |
+ else: |
442 |
+ for child, priorities in self.nodes[node][0].items(): |
443 |
+ if ignore_priority < priorities[-1]: |
444 |
+ children.append(child) |
445 |
+ return children |
446 |
+ |
447 |
+ def parent_nodes(self, node, ignore_priority=None): |
448 |
+ """Return all parents of the specified node""" |
449 |
+ if ignore_priority is None: |
450 |
+ return list(self.nodes[node][1]) |
451 |
+ parents = [] |
452 |
+ if hasattr(ignore_priority, '__call__'): |
453 |
+ for parent, priorities in self.nodes[node][1].items(): |
454 |
+ for priority in priorities: |
455 |
+ if not ignore_priority(priority): |
456 |
+ parents.append(parent) |
457 |
+ break |
458 |
+ else: |
459 |
+ for parent, priorities in self.nodes[node][1].items(): |
460 |
+ if ignore_priority < priorities[-1]: |
461 |
+ parents.append(parent) |
462 |
+ return parents |
463 |
+ |
464 |
+ def leaf_nodes(self, ignore_priority=None): |
465 |
+ """Return all nodes that have no children |
466 |
+ |
467 |
+ If ignore_soft_deps is True, soft deps are not counted as |
468 |
+ children in calculations.""" |
469 |
+ |
470 |
+ leaf_nodes = [] |
471 |
+ if ignore_priority is None: |
472 |
+ for node in self.order: |
473 |
+ if not self.nodes[node][0]: |
474 |
+ leaf_nodes.append(node) |
475 |
+ elif hasattr(ignore_priority, '__call__'): |
476 |
+ for node in self.order: |
477 |
+ is_leaf_node = True |
478 |
+ for child, priorities in self.nodes[node][0].items(): |
479 |
+ for priority in priorities: |
480 |
+ if not ignore_priority(priority): |
481 |
+ is_leaf_node = False |
482 |
+ break |
483 |
+ if not is_leaf_node: |
484 |
+ break |
485 |
+ if is_leaf_node: |
486 |
+ leaf_nodes.append(node) |
487 |
+ else: |
488 |
+ for node in self.order: |
489 |
+ is_leaf_node = True |
490 |
+ for child, priorities in self.nodes[node][0].items(): |
491 |
+ if ignore_priority < priorities[-1]: |
492 |
+ is_leaf_node = False |
493 |
+ break |
494 |
+ if is_leaf_node: |
495 |
+ leaf_nodes.append(node) |
496 |
+ return leaf_nodes |
497 |
+ |
498 |
+ def root_nodes(self, ignore_priority=None): |
499 |
+ """Return all nodes that have no parents. |
500 |
+ |
501 |
+ If ignore_soft_deps is True, soft deps are not counted as |
502 |
+ parents in calculations.""" |
503 |
+ |
504 |
+ root_nodes = [] |
505 |
+ if ignore_priority is None: |
506 |
+ for node in self.order: |
507 |
+ if not self.nodes[node][1]: |
508 |
+ root_nodes.append(node) |
509 |
+ elif hasattr(ignore_priority, '__call__'): |
510 |
+ for node in self.order: |
511 |
+ is_root_node = True |
512 |
+ for parent, priorities in self.nodes[node][1].items(): |
513 |
+ for priority in priorities: |
514 |
+ if not ignore_priority(priority): |
515 |
+ is_root_node = False |
516 |
+ break |
517 |
+ if not is_root_node: |
518 |
+ break |
519 |
+ if is_root_node: |
520 |
+ root_nodes.append(node) |
521 |
+ else: |
522 |
+ for node in self.order: |
523 |
+ is_root_node = True |
524 |
+ for parent, priorities in self.nodes[node][1].items(): |
525 |
+ if ignore_priority < priorities[-1]: |
526 |
+ is_root_node = False |
527 |
+ break |
528 |
+ if is_root_node: |
529 |
+ root_nodes.append(node) |
530 |
+ return root_nodes |
531 |
+ |
532 |
+ def is_empty(self): |
533 |
+ """Checks if the digraph is empty""" |
534 |
+ return len(self.nodes) == 0 |
535 |
+ |
536 |
+ def clone(self): |
537 |
+ clone = digraph() |
538 |
+ clone.nodes = {} |
539 |
+ memo = {} |
540 |
+ for children, parents, node in self.nodes.values(): |
541 |
+ children_clone = {} |
542 |
+ for child, priorities in children.items(): |
543 |
+ priorities_clone = memo.get(id(priorities)) |
544 |
+ if priorities_clone is None: |
545 |
+ priorities_clone = priorities[:] |
546 |
+ memo[id(priorities)] = priorities_clone |
547 |
+ children_clone[child] = priorities_clone |
548 |
+ parents_clone = {} |
549 |
+ for parent, priorities in parents.items(): |
550 |
+ priorities_clone = memo.get(id(priorities)) |
551 |
+ if priorities_clone is None: |
552 |
+ priorities_clone = priorities[:] |
553 |
+ memo[id(priorities)] = priorities_clone |
554 |
+ parents_clone[parent] = priorities_clone |
555 |
+ clone.nodes[node] = (children_clone, parents_clone, node) |
556 |
+ clone.order = self.order[:] |
557 |
+ return clone |
558 |
+ |
559 |
+ def delnode(self, node): |
560 |
+ try: |
561 |
+ self.remove(node) |
562 |
+ except KeyError: |
563 |
+ pass |
564 |
+ |
565 |
+ def firstzero(self): |
566 |
+ leaf_nodes = self.leaf_nodes() |
567 |
+ if leaf_nodes: |
568 |
+ return leaf_nodes[0] |
569 |
+ return None |
570 |
+ |
571 |
+ def hasallzeros(self, ignore_priority=None): |
572 |
+ return len(self.leaf_nodes(ignore_priority=ignore_priority)) == \ |
573 |
+ len(self.order) |
574 |
+ |
575 |
+ def debug_print(self): |
576 |
+ def output(s): |
577 |
+ writemsg(s, noiselevel=-1) |
578 |
+ for node in self.nodes: |
579 |
+ output("%s " % (node,)) |
580 |
+ if self.nodes[node][0]: |
581 |
+ output("depends on\n") |
582 |
+ else: |
583 |
+ output("(no children)\n") |
584 |
+ for child, priorities in self.nodes[node][0].items(): |
585 |
+ output(" %s (%s)\n" % (child, priorities[-1],)) |
586 |
+ |
587 |
+ # Backward compatibility |
588 |
+ addnode = add |
589 |
+ allnodes = all_nodes |
590 |
+ allzeros = leaf_nodes |
591 |
+ hasnode = contains |
592 |
+ __contains__ = contains |
593 |
+ empty = is_empty |
594 |
+ copy = clone |
595 |
|
596 |
|
597 |
Property changes on: main/trunk/pym/portage/util/digraph.py |
598 |
___________________________________________________________________ |
599 |
Added: svn:keywords |
600 |
+ Id |