/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to patches/progress.diff

  • Committer: Martin Pool
  • Date: 2005-08-24 08:59:32 UTC
  • Revision ID: mbp@sourcefrog.net-20050824085932-c61f1f1f1c930e13
- Add a simple UIFactory 

  The idea of this is to let a client of bzrlib set some 
  policy about how output is displayed.

  In this revision all that's done is that progress bars
  are constructed by a policy established by the application
  rather than being randomly constructed in the library 
  or passed down the calls.  This avoids progress bars
  popping up while running the test suite and cleans up
  some code.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
*** added file 'bzrlib/progress.py'
 
2
--- /dev/null 
 
3
+++ bzrlib/progress.py 
 
4
@@ -0,0 +1,138 @@
 
5
+# Copyright (C) 2005 Aaron Bentley
 
6
+# <aaron.bentley@utoronto.ca>
 
7
+#
 
8
+#    This program is free software; you can redistribute it and/or modify
 
9
+#    it under the terms of the GNU General Public License as published by
 
10
+#    the Free Software Foundation; either version 2 of the License, or
 
11
+#    (at your option) any later version.
 
12
+#
 
13
+#    This program is distributed in the hope that it will be useful,
 
14
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
+#    GNU General Public License for more details.
 
17
+#
 
18
+#    You should have received a copy of the GNU General Public License
 
19
+#    along with this program; if not, write to the Free Software
 
20
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
21
+
 
22
+import sys
 
23
+import datetime
 
24
+
 
25
+class Progress(object):
 
26
+    def __init__(self, units, current, total=None):
 
27
+        self.units = units
 
28
+        self.current = current
 
29
+        self.total = total
 
30
+
 
31
+    def _get_percent(self):
 
32
+        if self.total is not None and self.current is not None:
 
33
+            return 100.0 * self.current / self.total
 
34
+
 
35
+    percent = property(_get_percent)
 
36
+
 
37
+    def __str__(self):
 
38
+        if self.total is not None:
 
39
+            return "%i of %i %s %.1f%%" % (self.current, self.total, self.units,
 
40
+                                         self.percent)
 
41
+        else:
 
42
+            return "%i %s" (self.current, self.units) 
 
43
+
 
44
+class ProgressBar(object):
 
45
+    def __init__(self):
 
46
+        self.start = None
 
47
+        object.__init__(self)
 
48
+
 
49
+    def __call__(self, progress):
 
50
+        if self.start is None:
 
51
+            self.start = datetime.datetime.now()
 
52
+        progress_bar(progress, start_time=self.start)
 
53
+        
 
54
+def divide_timedelta(delt, divisor):
 
55
+    """Divides a timedelta object"""
 
56
+    return datetime.timedelta(float(delt.days)/divisor, 
 
57
+                              float(delt.seconds)/divisor, 
 
58
+                              float(delt.microseconds)/divisor)
 
59
+
 
60
+def str_tdelta(delt):
 
61
+    if delt is None:
 
62
+        return "-:--:--"
 
63
+    return str(datetime.timedelta(delt.days, delt.seconds))
 
64
+
 
65
+def get_eta(start_time, progress, enough_samples=20):
 
66
+    if start_time is None or progress.current == 0:
 
67
+        return None
 
68
+    elif progress.current < enough_samples:
 
69
+        return None
 
70
+    elapsed = datetime.datetime.now() - start_time
 
71
+    total_duration = divide_timedelta((elapsed) * long(progress.total), 
 
72
+                                      progress.current)
 
73
+    if elapsed < total_duration:
 
74
+        eta = total_duration - elapsed
 
75
+    else:
 
76
+        eta = total_duration - total_duration
 
77
+    return eta
 
78
+
 
79
+def progress_bar(progress, start_time=None):
 
80
+    eta = get_eta(start_time, progress)
 
81
+    if start_time is not None:
 
82
+        eta_str = " "+str_tdelta(eta)
 
83
+    else:
 
84
+        eta_str = ""
 
85
+
 
86
+    fmt = " %i of %i %s (%.1f%%)"
 
87
+    f = fmt % (progress.total, progress.total, progress.units, 100.0)
 
88
+    max = len(f)
 
89
+    cols = 77 - max
 
90
+    if start_time is not None:
 
91
+        cols -= len(eta_str)
 
92
+    markers = int (float(cols) * progress.current / progress.total)
 
93
+    txt = fmt % (progress.current, progress.total, progress.units,
 
94
+                 progress.percent)
 
95
+    sys.stderr.write("\r[%s%s]%s%s" % ('='*markers, ' '*(cols-markers), txt, 
 
96
+                                       eta_str))
 
97
+
 
98
+def clear_progress_bar():
 
99
+    sys.stderr.write('\r%s\r' % (' '*79))
 
100
+
 
101
+def spinner_str(progress, show_text=False):
 
102
+    """
 
103
+    Produces the string for a textual "spinner" progress indicator
 
104
+    :param progress: an object represinting current progress
 
105
+    :param show_text: If true, show progress text as well
 
106
+    :return: The spinner string
 
107
+
 
108
+    >>> spinner_str(Progress("baloons", 0))
 
109
+    '|'
 
110
+    >>> spinner_str(Progress("baloons", 5))
 
111
+    '/'
 
112
+    >>> spinner_str(Progress("baloons", 6), show_text=True)
 
113
+    '- 6 baloons'
 
114
+    """
 
115
+    positions = ('|', '/', '-', '\\')
 
116
+    text = positions[progress.current % 4]
 
117
+    if show_text:
 
118
+        text+=" %i %s" % (progress.current, progress.units)
 
119
+    return text
 
120
+
 
121
+def spinner(progress, show_text=False, output=sys.stderr):
 
122
+    """
 
123
+    Update a spinner progress indicator on an output
 
124
+    :param progress: The progress to display
 
125
+    :param show_text: If true, show text as well as spinner
 
126
+    :param output: The output to write to
 
127
+
 
128
+    >>> spinner(Progress("baloons", 6), show_text=True, output=sys.stdout)
 
129
+    \r- 6 baloons
 
130
+    """
 
131
+    output.write('\r%s' % spinner_str(progress, show_text))
 
132
+
 
133
+def run_tests():
 
134
+    import doctest
 
135
+    result = doctest.testmod()
 
136
+    if result[1] > 0:
 
137
+        if result[0] == 0:
 
138
+            print "All tests passed"
 
139
+    else:
 
140
+        print "No tests to run"
 
141
+if __name__ == "__main__":
 
142
+    run_tests()
 
143