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