~bzr-pqm/bzr/bzr.dev

4988.10.5 by John Arbash Meinel
Merge bzr.dev 5021 to resolve NEWS
1
# Copyright (C) 2005-2010 Canonical Ltd
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
648 by Martin Pool
- import aaron's progress-indicator code
16
649 by Martin Pool
- some cleanups for the progressbar method
17
3006.3.3 by Robert Collins
Docstring improvement and remove TODO's from progres.py.
18
"""Progress indicators.
19
20
The usual way to use this is via bzrlib.ui.ui_factory.nested_progress_bar which
3948.2.4 by Martin Pool
Remove some obsolete progress docstring
21
will manage a conceptual stack of nested activities.
649 by Martin Pool
- some cleanups for the progressbar method
22
"""
23
934 by Martin Pool
todo
24
648 by Martin Pool
- import aaron's progress-indicator code
25
import sys
660 by Martin Pool
- use plain unix time, not datetime module
26
import time
964 by Martin Pool
- show progress on dumb terminals by printing dots
27
import os
649 by Martin Pool
- some cleanups for the progressbar method
28
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
29
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
30
from bzrlib.symbol_versioning import (
31
    deprecated_in,
4712.1.2 by Martin Pool
Fix missing import
32
    deprecated_method,
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
33
    )
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
34
35
649 by Martin Pool
- some cleanups for the progressbar method
36
def _supports_progress(f):
4449.3.1 by Martin Pool
Un-soft-deprecate _supports_progress - still useful
37
    """Detect if we can use pretty progress bars on file F.
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
38
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
39
    If this returns true we expect that a human may be looking at that
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
40
    output, and that we can repaint a line to update it.
4449.3.1 by Martin Pool
Un-soft-deprecate _supports_progress - still useful
41
42
    This doesn't check the policy for whether we *should* use them.
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
43
    """
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
44
    isatty = getattr(f, 'isatty', None)
45
    if isatty is None:
695 by Martin Pool
- don't display progress bars on really dumb terminals
46
        return False
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
47
    if not isatty():
695 by Martin Pool
- don't display progress bars on really dumb terminals
48
        return False
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
49
    # The following case also handles Win32 - on that platform $TERM is
50
    # typically never set, so the case None is treated as a smart terminal,
51
    # not dumb.  <https://bugs.launchpad.net/bugs/334808>  win32 files do have
52
    # isatty methods that return true.
695 by Martin Pool
- don't display progress bars on really dumb terminals
53
    if os.environ.get('TERM') == 'dumb':
54
        # e.g. emacs compile window
55
        return False
56
    return True
649 by Martin Pool
- some cleanups for the progressbar method
57
58
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
59
class ProgressTask(object):
60
    """Model component of a progress indicator.
61
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
62
    Most code that needs to indicate progress should update one of these,
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
63
    and it will in turn update the display, if one is present.
3882.8.5 by Martin Pool
Progress tasks can indicate what kind of display is useful
64
65
    Code updating the task may also set fields as hints about how to display
66
    it: show_pct, show_spinner, show_eta, show_count, show_bar.  UIs
67
    will not necessarily respect all these fields.
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
68
    
69
    :ivar update_latency: The interval (in seconds) at which the PB should be
70
        updated.  Setting this to zero suggests every update should be shown
71
        synchronously.
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
72
73
    :ivar show_transport_activity: If true (default), transport activity
74
        will be shown when this task is drawn.  Disable it if you're sure 
75
        that only irrelevant or uninteresting transport activity can occur
76
        during this task.
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
77
    """
78
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
79
    def __init__(self, parent_task=None, ui_factory=None, progress_view=None):
4110.2.13 by Martin Pool
doc
80
        """Construct a new progress task.
81
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
82
        :param parent_task: Enclosing ProgressTask or None.
83
84
        :param progress_view: ProgressView to display this ProgressTask.
85
86
        :param ui_factory: The UI factory that will display updates; 
87
            deprecated in favor of passing progress_view directly.
88
4110.2.13 by Martin Pool
doc
89
        Normally you should not call this directly but rather through
90
        `ui_factory.nested_progress_bar`.
91
        """
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
92
        self._parent_task = parent_task
93
        self._last_update = 0
94
        self.total_cnt = None
95
        self.current_cnt = None
96
        self.msg = ''
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
97
        # TODO: deprecate passing ui_factory
3882.8.2 by Martin Pool
ProgressTask holds a reference to the ui that displays it
98
        self.ui_factory = ui_factory
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
99
        self.progress_view = progress_view
3882.8.5 by Martin Pool
Progress tasks can indicate what kind of display is useful
100
        self.show_pct = False
101
        self.show_spinner = True
102
        self.show_eta = False,
103
        self.show_count = True
104
        self.show_bar = True
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
105
        self.update_latency = 0.1
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
106
        self.show_transport_activity = True
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
107
3948.2.1 by Martin Pool
Add ProgressTask repr
108
    def __repr__(self):
109
        return '%s(%r/%r, msg=%r)' % (
110
            self.__class__.__name__,
111
            self.current_cnt,
112
            self.total_cnt,
113
            self.msg)
114
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
115
    def update(self, msg, current_cnt=None, total_cnt=None):
116
        self.msg = msg
117
        self.current_cnt = current_cnt
118
        if total_cnt:
119
            self.total_cnt = total_cnt
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
120
        if self.progress_view:
121
            self.progress_view.show_progress(self)
122
        else:
123
            self.ui_factory._progress_updated(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
124
3882.8.8 by Martin Pool
Progress and UI test cleanups
125
    def tick(self):
126
        self.update(self.msg)
127
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
128
    def finished(self):
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
129
        if self.progress_view:
130
            self.progress_view.task_finished(self)
131
        else:
132
            self.ui_factory._progress_finished(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
133
134
    def make_sub_task(self):
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
135
        return ProgressTask(self, ui_factory=self.ui_factory,
136
            progress_view=self.progress_view)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
137
138
    def _overall_completion_fraction(self, child_fraction=0.0):
139
        """Return fractional completion of this task and its parents
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
140
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
141
        Returns None if no completion can be computed."""
4017.1.1 by John Arbash Meinel
Get a pb.tick() to work after calling pb.update()
142
        if self.current_cnt is not None and self.total_cnt:
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
143
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
144
        else:
4110.2.17 by Martin Pool
If one ProgressTask has no count, it passes through that of its child
145
            # if this task has no estimation, it just passes on directly
146
            # whatever the child has measured...
147
            own_fraction = child_fraction
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
148
        if self._parent_task is None:
149
            return own_fraction
150
        else:
151
            if own_fraction is None:
152
                own_fraction = 0.0
153
            return self._parent_task._overall_completion_fraction(own_fraction)
154
4712.1.1 by Martin Pool
merge old deprecation of ProgressTask.note
155
    @deprecated_method(deprecated_in((2, 1, 0)))
3882.8.4 by Martin Pool
All UI factories should support note()
156
    def note(self, fmt_string, *args):
4471.2.2 by Martin Pool
Deprecate ProgressTask.note
157
        """Record a note without disrupting the progress bar.
158
        
159
        Deprecated: use ui_factory.note() instead or bzrlib.trace.  Note that
160
        ui_factory.note takes just one string as the argument, not a format
161
        string and arguments.
162
        """
3943.2.3 by Martin Pool
Don't do string interpolation if there are no arguments
163
        if args:
164
            self.ui_factory.note(fmt_string % args)
165
        else:
166
            self.ui_factory.note(fmt_string)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
167
168
    def clear(self):
4471.2.1 by Martin Pool
Comment on deprecating ProgressTask.clear
169
        # TODO: deprecate this method; the model object shouldn't be concerned
170
        # with whether it's shown or not.  Most callers use this because they
171
        # want to write some different non-progress output to the screen, but
172
        # they should probably instead use a stream that's synchronized with
173
        # the progress output.  It may be there is a model-level use for
174
        # saying "this task's not active at the moment" but I don't see it. --
175
        # mbp 20090623
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
176
        if self.progress_view:
177
            self.progress_view.clear()
178
        else:
179
            self.ui_factory.clear_term()
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
180
649 by Martin Pool
- some cleanups for the progressbar method
181
4449.3.5 by Martin Pool
doc
182
# NOTE: This is also deprecated; you should provide a ProgressView instead.
964 by Martin Pool
- show progress on dumb terminals by printing dots
183
class _BaseProgressBar(object):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
184
964 by Martin Pool
- show progress on dumb terminals by printing dots
185
    def __init__(self,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
186
                 to_file=None,
964 by Martin Pool
- show progress on dumb terminals by printing dots
187
                 show_pct=False,
188
                 show_spinner=False,
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
189
                 show_eta=False,
964 by Martin Pool
- show progress on dumb terminals by printing dots
190
                 show_bar=True,
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
191
                 show_count=True,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
192
                 to_messages_file=None,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
193
                 _stack=None):
964 by Martin Pool
- show progress on dumb terminals by printing dots
194
        object.__init__(self)
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
195
        if to_file is None:
196
            to_file = sys.stderr
197
        if to_messages_file is None:
198
            to_messages_file = sys.stdout
964 by Martin Pool
- show progress on dumb terminals by printing dots
199
        self.to_file = to_file
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
200
        self.to_messages_file = to_messages_file
964 by Martin Pool
- show progress on dumb terminals by printing dots
201
        self.last_msg = None
202
        self.last_cnt = None
203
        self.last_total = None
204
        self.show_pct = show_pct
205
        self.show_spinner = show_spinner
206
        self.show_eta = show_eta
207
        self.show_bar = show_bar
208
        self.show_count = show_count
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
209
        self._stack = _stack
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
210
        # seed throttler
211
        self.MIN_PAUSE = 0.1 # seconds
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
212
        now = time.time()
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
213
        # starting now
2745.6.52 by Andrew Bennetts
Revert bad change to bzrlib/progress.py
214
        self.start_time = now
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
215
        # next update should not throttle
216
        self.last_update = now - self.MIN_PAUSE - 1
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
217
218
    def finished(self):
219
        """Return this bar to its progress stack."""
220
        self.clear()
221
        self._stack.return_pb(self)
1104 by Martin Pool
- Add a simple UIFactory
222
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
223
    def note(self, fmt_string, *args, **kwargs):
224
        """Record a note without disrupting the progress bar."""
1558.8.5 by Aaron Bentley
Pass note up the stack instead of using bzrlib.ui_factory
225
        self.clear()
1558.7.9 by Aaron Bentley
Bad change. (broke tests). Reverted.
226
        self.to_messages_file.write(fmt_string % args)
227
        self.to_messages_file.write('\n')
1104 by Martin Pool
- Add a simple UIFactory
228
1534.11.7 by Robert Collins
Test and correct the problem with nested test logs breaking further in-test logs.
229
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
230
class DummyProgress(object):
1104 by Martin Pool
- Add a simple UIFactory
231
    """Progress-bar standin that does nothing.
232
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
233
    This was previously often constructed by application code if no progress
234
    bar was explicitly passed in.  That's no longer recommended: instead, just
235
    create a progress task from the ui_factory.  This class can be used in
236
    test code that needs to fake a progress task for some reason.
237
    """
3882.8.8 by Martin Pool
Progress and UI test cleanups
238
1104 by Martin Pool
- Add a simple UIFactory
239
    def tick(self):
240
        pass
241
242
    def update(self, msg=None, current=None, total=None):
243
        pass
244
1551.2.27 by Aaron Bentley
Got propogation under test
245
    def child_update(self, message, current, total):
246
        pass
247
1104 by Martin Pool
- Add a simple UIFactory
248
    def clear(self):
249
        pass
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
250
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
251
    def note(self, fmt_string, *args, **kwargs):
252
        """See _BaseProgressBar.note()."""
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
253
1551.2.29 by Aaron Bentley
Got stack handling under test
254
    def child_progress(self, **kwargs):
255
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
256
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
257
648 by Martin Pool
- import aaron's progress-indicator code
258
def str_tdelta(delt):
259
    if delt is None:
260
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
261
    delt = int(round(delt))
262
    return '%d:%02d:%02d' % (delt/3600,
263
                             (delt/60) % 60,
264
                             delt % 60)
265
266
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
267
def get_eta(start_time, current, total, enough_samples=3, last_updates=None, n_recent=10):
660 by Martin Pool
- use plain unix time, not datetime module
268
    if start_time is None:
269
        return None
270
271
    if not total:
272
        return None
273
274
    if current < enough_samples:
275
        return None
276
277
    if current > total:
278
        return None                     # wtf?
279
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
280
    elapsed = time.time() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
281
282
    if elapsed < 2.0:                   # not enough time to estimate
283
        return None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
284
660 by Martin Pool
- use plain unix time, not datetime module
285
    total_duration = float(elapsed) * float(total) / float(current)
286
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
287
    if last_updates and len(last_updates) >= n_recent:
288
        avg = sum(last_updates) / float(len(last_updates))
289
        time_left = avg * (total - current)
290
291
        old_time_left = total_duration - elapsed
292
293
        # We could return the average, or some other value here
294
        return (time_left + old_time_left) / 2
295
660 by Martin Pool
- use plain unix time, not datetime module
296
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
297
649 by Martin Pool
- some cleanups for the progressbar method
298
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
299
class ProgressPhase(object):
300
    """Update progress object with the current phase"""
301
    def __init__(self, message, total, pb):
302
        object.__init__(self)
303
        self.pb = pb
304
        self.message = message
305
        self.total = total
306
        self.cur_phase = None
307
308
    def next_phase(self):
309
        if self.cur_phase is None:
310
            self.cur_phase = 0
311
        else:
312
            self.cur_phase += 1
313
        self.pb.update(self.message, self.cur_phase, self.total)