~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to patches/progress.diff

  • Committer: Martin Pool
  • Date: 2005-06-29 02:55:33 UTC
  • Revision ID: mbp@sourcefrog.net-20050629025533-c8fb62423361c8a2
Patch from John:

- StringIO mixes poorly with difflib

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