~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tsort.py

Clean up the lock.py code to use less indenting, and conform to better coding practise.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""Topological sorting routines."""
19
19
 
20
20
 
21
 
from bzrlib import errors
 
21
import bzrlib.errors as errors
22
22
 
23
23
 
24
24
__all__ = ["topo_sort", "TopoSorter", "merge_sort", "MergeSorter"]
61
61
        """
62
62
        # a dict of the graph.
63
63
        self._graph = dict(graph)
64
 
        self._visitable = set(self._graph)
65
64
        ### if debugging:
66
65
        # self._original_graph = dict(graph)
67
66
        
121
120
                            # this parent was completed by a child on the
122
121
                            # call stack. skip it.
123
122
                            continue
124
 
                        if next_node_name not in self._visitable:
125
 
                            continue
126
123
                        # otherwise transfer it from the source graph into the
127
124
                        # top of the current depth first search stack.
128
125
                        try:
189
186
 
190
187
class MergeSorter(object):
191
188
 
192
 
    __slots__ = ['_node_name_stack',
193
 
                 '_node_merge_depth_stack',
194
 
                 '_pending_parents_stack',
195
 
                 '_assigned_sequence_stack',
196
 
                 '_left_subtree_pushed_stack',
197
 
                 '_generate_revno',
198
 
                 '_graph',
199
 
                 '_mainline_revisions',
200
 
                 '_stop_revision',
201
 
                 '_original_graph',
202
 
                 '_revnos',
203
 
                 '_root_sequence',
204
 
                 '_completed_node_names',
205
 
                 '_scheduled_nodes',
206
 
                ]
207
 
 
208
189
    def __init__(self, graph, branch_tip, mainline_revisions=None,
209
190
        generate_revno=False):
210
191
        """Merge-aware topological sorting of a graph.
423
404
        After finishing iteration the sorter is empty and you cannot continue
424
405
        iteration.
425
406
        """
426
 
        # These are safe to offload to local variables, because they are used
427
 
        # as a stack and modified in place, never assigned to.
428
 
        node_name_stack = self._node_name_stack
429
 
        node_merge_depth_stack = self._node_merge_depth_stack
430
 
        pending_parents_stack = self._pending_parents_stack
431
 
        left_subtree_pushed_stack = self._left_subtree_pushed_stack
432
 
        completed_node_names = self._completed_node_names
433
 
        scheduled_nodes = self._scheduled_nodes
434
 
 
435
 
        graph_pop = self._graph.pop
436
 
 
437
 
        def push_node(node_name, merge_depth, parents,
438
 
                      node_name_stack_append=node_name_stack.append,
439
 
                      node_merge_depth_stack_append=node_merge_depth_stack.append,
440
 
                      left_subtree_pushed_stack_append=left_subtree_pushed_stack.append,
441
 
                      pending_parents_stack_append=pending_parents_stack.append,
442
 
                      assigned_sequence_stack_append=self._assigned_sequence_stack.append,
443
 
                      original_graph=self._original_graph,
444
 
                      revnos=self._revnos,
445
 
                      ):
446
 
            """Add node_name to the pending node stack.
447
 
 
448
 
            Names in this stack will get emitted into the output as they are popped
449
 
            off the stack.
450
 
 
451
 
            This inlines a lot of self._variable.append functions as local
452
 
            variables.
453
 
            """
454
 
            node_name_stack_append(node_name)
455
 
            node_merge_depth_stack_append(merge_depth)
456
 
            left_subtree_pushed_stack_append(False)
457
 
            pending_parents_stack_append(list(parents))
458
 
            # as we push it, assign it a sequence number against its parent:
459
 
            parents = original_graph[node_name]
460
 
            if parents:
461
 
                # node has parents, assign from the left most parent.
462
 
                parent_revno = revnos[parents[0]]
463
 
                sequence = parent_revno[1]
464
 
                parent_revno[1] += 1
465
 
            else:
466
 
                # no parents, use the root sequence
467
 
                sequence = self._root_sequence
468
 
                self._root_sequence +=1
469
 
            assigned_sequence_stack_append(sequence)
470
 
 
471
 
        def pop_node(node_name_stack_pop=node_name_stack.pop,
472
 
                     node_merge_depth_stack_pop=node_merge_depth_stack.pop,
473
 
                     assigned_sequence_stack_pop=self._assigned_sequence_stack.pop,
474
 
                     left_subtree_pushed_stack_pop=left_subtree_pushed_stack.pop,
475
 
                     pending_parents_stack_pop=pending_parents_stack.pop,
476
 
                     original_graph=self._original_graph,
477
 
                     revnos=self._revnos,
478
 
                     completed_node_names_add=self._completed_node_names.add,
479
 
                     scheduled_nodes_append=scheduled_nodes.append,
480
 
                    ):
481
 
            """Pop the top node off the stack
482
 
 
483
 
            The node is appended to the sorted output.
484
 
            """
485
 
            # we are returning from the flattened call frame:
486
 
            # pop off the local variables
487
 
            node_name = node_name_stack_pop()
488
 
            merge_depth = node_merge_depth_stack_pop()
489
 
            sequence = assigned_sequence_stack_pop()
490
 
            # remove this node from the pending lists:
491
 
            left_subtree_pushed_stack_pop()
492
 
            pending_parents_stack_pop()
493
 
 
494
 
            parents = original_graph[node_name]
495
 
            if parents:
496
 
                # node has parents, assign from the left most parent.
497
 
                parent_revno = revnos[parents[0]]
498
 
                if sequence:
499
 
                    # not the first child, make a new branch
500
 
                    revno = parent_revno[0] + (sequence, 1)
501
 
                else:
502
 
                    # increment the sequence number within the branch
503
 
                    revno = parent_revno[0][:-1] + (parent_revno[0][-1] + 1,)
504
 
            else:
505
 
                # no parents, use the root sequence
506
 
                if sequence:
507
 
                    # make a parallel import revision number
508
 
                    revno = (0, sequence, 1)
509
 
                else:
510
 
                    revno = (1,)
511
 
 
512
 
            # store the revno for this node for future reference
513
 
            revnos[node_name][0] = revno
514
 
            completed_node_names_add(node_name)
515
 
            scheduled_nodes_append((node_name, merge_depth, revno))
516
 
            return node_name
517
 
 
518
 
 
519
 
        while node_name_stack:
 
407
        while self._node_name_stack:
520
408
            # loop until this call completes.
521
 
            parents_to_visit = pending_parents_stack[-1]
 
409
            parents_to_visit = self._pending_parents_stack[-1]
522
410
            # if all parents are done, the revision is done
523
411
            if not parents_to_visit:
524
412
                # append the revision to the topo sorted scheduled list:
525
413
                # all the nodes parents have been scheduled added, now
526
414
                # we can add it to the output.
527
 
                pop_node()
 
415
                self._pop_node()
528
416
            else:
529
 
                while pending_parents_stack[-1]:
530
 
                    if not left_subtree_pushed_stack[-1]:
 
417
                while self._pending_parents_stack[-1]:
 
418
                    if not self._left_subtree_pushed_stack[-1]:
531
419
                        # recurse depth first into the primary parent
532
 
                        next_node_name = pending_parents_stack[-1].pop(0)
 
420
                        next_node_name = self._pending_parents_stack[-1].pop(0)
533
421
                    else:
534
422
                        # place any merges in right-to-left order for scheduling
535
423
                        # which gives us left-to-right order after we reverse
538
426
                        # subtree rather than the left most, which will 
539
427
                        # display nicely (you get smaller trees at the top
540
428
                        # of the combined merge).
541
 
                        next_node_name = pending_parents_stack[-1].pop()
542
 
                    if next_node_name in completed_node_names:
 
429
                        next_node_name = self._pending_parents_stack[-1].pop()
 
430
                    if next_node_name in self._completed_node_names:
543
431
                        # this parent was completed by a child on the
544
432
                        # call stack. skip it.
545
433
                        continue
546
434
                    # otherwise transfer it from the source graph into the
547
435
                    # top of the current depth first search stack.
548
436
                    try:
549
 
                        parents = graph_pop(next_node_name)
 
437
                        parents = self._graph.pop(next_node_name)
550
438
                    except KeyError:
551
439
                        # if the next node is not in the source graph it has
552
440
                        # already been popped from it and placed into the
553
441
                        # current search stack (but not completed or we would
554
442
                        # have hit the continue 4 lines up.
555
443
                        # this indicates a cycle.
556
 
                        raise errors.GraphCycleError(node_name_stack)
 
444
                        raise errors.GraphCycleError(self._node_name_stack)
557
445
                    next_merge_depth = 0
558
 
                    if left_subtree_pushed_stack[-1]:
 
446
                    if self._left_subtree_pushed_stack[-1]:
559
447
                        # a new child branch from name_stack[-1]
560
448
                        next_merge_depth = 1
561
449
                    else:
562
450
                        next_merge_depth = 0
563
 
                        left_subtree_pushed_stack[-1] = True
 
451
                        self._left_subtree_pushed_stack[-1] = True
564
452
                    next_merge_depth = (
565
 
                        node_merge_depth_stack[-1] + next_merge_depth)
566
 
                    push_node(
 
453
                        self._node_merge_depth_stack[-1] + next_merge_depth)
 
454
                    self._push_node(
567
455
                        next_node_name,
568
456
                        next_merge_depth,
569
457
                        parents)
570
458
                    # and do not continue processing parents until this 'call' 
571
459
                    # has recursed.
572
460
                    break
573
 
 
574
461
        # We have scheduled the graph. Now deliver the ordered output:
575
462
        sequence_number = 0
576
 
        stop_revision = self._stop_revision
577
 
        generate_revno = self._generate_revno
578
 
        original_graph = self._original_graph
579
 
 
580
 
        while scheduled_nodes:
581
 
            node_name, merge_depth, revno = scheduled_nodes.pop()
582
 
            if node_name == stop_revision:
 
463
        while self._scheduled_nodes:
 
464
            node_name, merge_depth, revno = self._scheduled_nodes.pop()
 
465
            if node_name == self._stop_revision:
583
466
                return
584
 
            if not len(scheduled_nodes):
 
467
            if not len(self._scheduled_nodes):
585
468
                # last revision is the end of a merge
586
469
                end_of_merge = True
587
 
            elif scheduled_nodes[-1][1] < merge_depth:
 
470
            elif self._scheduled_nodes[-1][1] < merge_depth:
588
471
                # the next node is to our left
589
472
                end_of_merge = True
590
 
            elif (scheduled_nodes[-1][1] == merge_depth and
591
 
                  (scheduled_nodes[-1][0] not in
592
 
                   original_graph[node_name])):
 
473
            elif (self._scheduled_nodes[-1][1] == merge_depth and
 
474
                  (self._scheduled_nodes[-1][0] not in
 
475
                   self._original_graph[node_name])):
593
476
                # the next node was part of a multiple-merge.
594
477
                end_of_merge = True
595
478
            else:
596
479
                end_of_merge = False
597
 
            if generate_revno:
 
480
            if self._generate_revno:
598
481
                yield (sequence_number, node_name, merge_depth, revno, end_of_merge)
599
482
            else:
600
483
                yield (sequence_number, node_name, merge_depth, end_of_merge)