~bzr-pqm/bzr/bzr.dev

645 by Martin Pool
- split out proposed progress module
1
*** added file 'bzrlib/progress.py'
2
--- /dev/null 
3
+++ bzrlib/progress.py 
647 by Martin Pool
- split out updated progress indicator
4
@@ -0,0 +1,138 @@
645 by Martin Pool
- split out proposed progress module
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
647 by Martin Pool
- split out updated progress indicator
23
+import datetime
645 by Martin Pool
- split out proposed progress module
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
647 by Martin Pool
- split out updated progress indicator
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)
645 by Martin Pool
- split out proposed progress module
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
+
647 by Martin Pool
- split out updated progress indicator
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
+
645 by Martin Pool
- split out proposed progress module
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
647 by Martin Pool
- split out updated progress indicator
90
+    if start_time is not None:
91
+        cols -= len(eta_str)
645 by Martin Pool
- split out proposed progress module
92
+    markers = int (float(cols) * progress.current / progress.total)
93
+    txt = fmt % (progress.current, progress.total, progress.units,
94
+                 progress.percent)
647 by Martin Pool
- split out updated progress indicator
95
+    sys.stderr.write("\r[%s%s]%s%s" % ('='*markers, ' '*(cols-markers), txt, 
96
+                                       eta_str))
645 by Martin Pool
- split out proposed progress module
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