OLD | NEW |
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 from __future__ import with_statement | 5 from __future__ import with_statement |
6 | 6 |
7 import errno | 7 import errno |
8 import filecmp | 8 import filecmp |
9 import os.path | 9 import os.path |
10 import re | 10 import re |
(...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 if idfun is None: | 394 if idfun is None: |
395 def idfun(x): return x | 395 def idfun(x): return x |
396 seen = {} | 396 seen = {} |
397 result = [] | 397 result = [] |
398 for item in seq: | 398 for item in seq: |
399 marker = idfun(item) | 399 marker = idfun(item) |
400 if marker in seen: continue | 400 if marker in seen: continue |
401 seen[marker] = 1 | 401 seen[marker] = 1 |
402 result.append(item) | 402 result.append(item) |
403 return result | 403 return result |
| 404 |
| 405 |
| 406 class CycleError(Exception): |
| 407 """An exception raised when an unexpected cycle is detected.""" |
| 408 def __init__(self, nodes): |
| 409 self.nodes = nodes |
| 410 def __str__(self): |
| 411 return 'CycleError: cycle involving: ' + str(self.nodes) |
| 412 |
| 413 |
| 414 def TopologicallySorted(graph, get_edges): |
| 415 """Topologically sort based on a user provided edge definition. |
| 416 |
| 417 Args: |
| 418 graph: A list of node names. |
| 419 get_edges: A function mapping from node name to a hashable collection |
| 420 of node names which this node has outgoing edges to. |
| 421 Returns: |
| 422 A list containing all of the node in graph in topological order. |
| 423 It is assumed that calling get_edges once for each node and caching is |
| 424 cheaper than repeatedly calling get_edges. |
| 425 Raises: |
| 426 CycleError in the event of a cycle. |
| 427 Example: |
| 428 graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'} |
| 429 def GetEdges(node): |
| 430 return re.findall(r'\$\(([^))]\)', graph[node]) |
| 431 print TopologicallySorted(graph.keys(), GetEdges) |
| 432 ==> |
| 433 ['a', 'c', b'] |
| 434 """ |
| 435 get_edges = memoize(get_edges) |
| 436 visited = set() |
| 437 visiting = set() |
| 438 ordered_nodes = [] |
| 439 def Visit(node): |
| 440 if node in visiting: |
| 441 raise CycleError(visiting) |
| 442 if node in visited: |
| 443 return |
| 444 visited.add(node) |
| 445 visiting.add(node) |
| 446 for neighbor in get_edges(node): |
| 447 Visit(neighbor) |
| 448 visiting.remove(node) |
| 449 ordered_nodes.insert(0, node) |
| 450 for node in sorted(graph): |
| 451 Visit(node) |
| 452 return ordered_nodes |
OLD | NEW |