/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 bzrlib/progress.py

  • Committer: Martin Pool
  • Date: 2005-06-10 07:19:12 UTC
  • Revision ID: mbp@sourcefrog.net-20050610071911-97fa22f079414945
- update check command to use aaron's progress code
  rather than hand-hacked progress indicator

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 Aaron Bentley <aaron.bentley@utoronto.ca>
 
2
# Copyright (C) 2005 Canonical <canonical.com>
 
3
#
 
4
#    This program is free software; you can redistribute it and/or modify
 
5
#    it under the terms of the GNU General Public License as published by
 
6
#    the Free Software Foundation; either version 2 of the License, or
 
7
#    (at your option) any later version.
 
8
#
 
9
#    This program is distributed in the hope that it will be useful,
 
10
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#    GNU General Public License for more details.
 
13
#
 
14
#    You should have received a copy of the GNU General Public License
 
15
#    along with this program; if not, write to the Free Software
 
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
 
 
19
"""
 
20
Simple text-mode progress indicator.
 
21
 
 
22
Everyone loves ascii art!
 
23
 
 
24
To display an indicator, create a ProgressBar object.  Call it,
 
25
passing Progress objects indicating the current state.  When done,
 
26
call clear().
 
27
 
 
28
Progress is suppressed when output is not sent to a terminal, so as
 
29
not to clutter log files.
 
30
"""
 
31
 
 
32
# TODO: remove functions in favour of keeping everything in one class
 
33
 
 
34
# TODO: should be a global option e.g. --silent that disables progress
 
35
# indicators, preferably without needing to adjust all code that
 
36
# potentially calls them.
 
37
 
 
38
 
 
39
import sys
 
40
import datetime
 
41
 
 
42
 
 
43
def _width():
 
44
    """Return estimated terminal width.
 
45
 
 
46
    TODO: Do something smart on Windows?
 
47
 
 
48
    TODO: Is there anything that gets a better update when the window
 
49
          is resized while the program is running?
 
50
    """
 
51
    import os
 
52
    try:
 
53
        return int(os.environ['COLUMNS'])
 
54
    except (IndexError, KeyError, ValueError):
 
55
        return 80
 
56
 
 
57
 
 
58
def _supports_progress(f):
 
59
    return hasattr(f, 'isatty') and f.isatty()
 
60
 
 
61
 
 
62
 
 
63
class Progress(object):
 
64
    """Description of progress through a task.
 
65
 
 
66
    Basically just a fancy tuple holding:
 
67
 
 
68
    units
 
69
        noun string describing what is being traversed, e.g.
 
70
        "balloons", "kB"
 
71
 
 
72
    current
 
73
        how many objects have been processed so far
 
74
 
 
75
    total
 
76
        total number of objects to process, if known.
 
77
    """
 
78
    
 
79
    def __init__(self, units, current, total=None):
 
80
        self.units = units
 
81
        self.current = current
 
82
        self.total = total
 
83
 
 
84
    def _get_percent(self):
 
85
        if self.total is not None and self.current is not None:
 
86
            return 100.0 * self.current / self.total
 
87
 
 
88
    percent = property(_get_percent)
 
89
 
 
90
    def __str__(self):
 
91
        if self.total is not None:
 
92
            return "%i of %i %s %.1f%%" % (self.current, self.total, self.units,
 
93
                                         self.percent)
 
94
        else:
 
95
            return "%i %s" (self.current, self.units)
 
96
 
 
97
 
 
98
 
 
99
class ProgressBar(object):
 
100
    def __init__(self, to_file=sys.stderr):
 
101
        object.__init__(self)
 
102
        self.start = None
 
103
        self.to_file = to_file
 
104
        self.suppressed = not _supports_progress(self.to_file)
 
105
 
 
106
 
 
107
    def __call__(self, progress):
 
108
        if self.start is None:
 
109
            self.start = datetime.datetime.now()
 
110
        if not self.suppressed:
 
111
            draw_progress_bar(progress, start_time=self.start,
 
112
                              to_file=self.to_file)
 
113
 
 
114
    def clear(self):
 
115
        if not self.suppressed:
 
116
            clear_progress_bar(self.to_file)
 
117
    
 
118
 
 
119
        
 
120
def divide_timedelta(delt, divisor):
 
121
    """Divides a timedelta object"""
 
122
    return datetime.timedelta(float(delt.days)/divisor, 
 
123
                              float(delt.seconds)/divisor, 
 
124
                              float(delt.microseconds)/divisor)
 
125
 
 
126
def str_tdelta(delt):
 
127
    if delt is None:
 
128
        return "-:--:--"
 
129
    return str(datetime.timedelta(delt.days, delt.seconds))
 
130
 
 
131
 
 
132
def get_eta(start_time, progress, enough_samples=20):
 
133
    if start_time is None or progress.current == 0:
 
134
        return None
 
135
    elif progress.current < enough_samples:
 
136
        return None
 
137
    elapsed = datetime.datetime.now() - start_time
 
138
    total_duration = divide_timedelta((elapsed) * long(progress.total), 
 
139
                                      progress.current)
 
140
    if elapsed < total_duration:
 
141
        eta = total_duration - elapsed
 
142
    else:
 
143
        eta = total_duration - total_duration
 
144
    return eta
 
145
 
 
146
 
 
147
def draw_progress_bar(progress, start_time=None, to_file=sys.stderr):
 
148
    eta = get_eta(start_time, progress)
 
149
    if start_time is not None:
 
150
        eta_str = " "+str_tdelta(eta)
 
151
    else:
 
152
        eta_str = ""
 
153
 
 
154
    fmt = " %i of %i %s (%.1f%%)"
 
155
    f = fmt % (progress.total, progress.total, progress.units, 100.0)
 
156
    cols = _width() - 3 - len(f)
 
157
    if start_time is not None:
 
158
        cols -= len(eta_str)
 
159
    markers = int (float(cols) * progress.current / progress.total)
 
160
    txt = fmt % (progress.current, progress.total, progress.units,
 
161
                 progress.percent)
 
162
    to_file.write("\r[%s%s]%s%s" % ('='*markers, ' '*(cols-markers), txt, 
 
163
                                       eta_str))
 
164
 
 
165
def clear_progress_bar(to_file=sys.stderr):
 
166
    to_file.write('\r%s\r' % (' '*79))
 
167
 
 
168
 
 
169
def spinner_str(progress, show_text=False):
 
170
    """
 
171
    Produces the string for a textual "spinner" progress indicator
 
172
    :param progress: an object represinting current progress
 
173
    :param show_text: If true, show progress text as well
 
174
    :return: The spinner string
 
175
 
 
176
    >>> spinner_str(Progress("baloons", 0))
 
177
    '|'
 
178
    >>> spinner_str(Progress("baloons", 5))
 
179
    '/'
 
180
    >>> spinner_str(Progress("baloons", 6), show_text=True)
 
181
    '- 6 baloons'
 
182
    """
 
183
    positions = ('|', '/', '-', '\\')
 
184
    text = positions[progress.current % 4]
 
185
    if show_text:
 
186
        text+=" %i %s" % (progress.current, progress.units)
 
187
    return text
 
188
 
 
189
 
 
190
def spinner(progress, show_text=False, output=sys.stderr):
 
191
    """
 
192
    Update a spinner progress indicator on an output
 
193
    :param progress: The progress to display
 
194
    :param show_text: If true, show text as well as spinner
 
195
    :param output: The output to write to
 
196
 
 
197
    >>> spinner(Progress("baloons", 6), show_text=True, output=sys.stdout)
 
198
    \r- 6 baloons
 
199
    """
 
200
    output.write('\r%s' % spinner_str(progress, show_text))
 
201
 
 
202
 
 
203
def run_tests():
 
204
    import doctest
 
205
    result = doctest.testmod()
 
206
    if result[1] > 0:
 
207
        if result[0] == 0:
 
208
            print "All tests passed"
 
209
    else:
 
210
        print "No tests to run"
 
211
 
 
212
 
 
213
def demo():
 
214
    from time import sleep
 
215
    pb = ProgressBar()
 
216
    for i in range(100):
 
217
        pb(Progress('Elephanten', i, 100))
 
218
        sleep(0.3)
 
219
    print 'done!'
 
220
 
 
221
if __name__ == "__main__":
 
222
    demo()