~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
155
    def clear(self):
4471.2.1 by Martin Pool
Comment on deprecating ProgressTask.clear
156
        # TODO: deprecate this method; the model object shouldn't be concerned
157
        # with whether it's shown or not.  Most callers use this because they
158
        # want to write some different non-progress output to the screen, but
159
        # they should probably instead use a stream that's synchronized with
160
        # the progress output.  It may be there is a model-level use for
161
        # saying "this task's not active at the moment" but I don't see it. --
162
        # mbp 20090623
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
163
        if self.progress_view:
164
            self.progress_view.clear()
165
        else:
166
            self.ui_factory.clear_term()
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
167
649 by Martin Pool
- some cleanups for the progressbar method
168
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
169
class DummyProgress(object):
1104 by Martin Pool
- Add a simple UIFactory
170
    """Progress-bar standin that does nothing.
171
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
172
    This was previously often constructed by application code if no progress
173
    bar was explicitly passed in.  That's no longer recommended: instead, just
174
    create a progress task from the ui_factory.  This class can be used in
175
    test code that needs to fake a progress task for some reason.
176
    """
3882.8.8 by Martin Pool
Progress and UI test cleanups
177
1104 by Martin Pool
- Add a simple UIFactory
178
    def tick(self):
179
        pass
180
181
    def update(self, msg=None, current=None, total=None):
182
        pass
183
1551.2.27 by Aaron Bentley
Got propogation under test
184
    def child_update(self, message, current, total):
185
        pass
186
1104 by Martin Pool
- Add a simple UIFactory
187
    def clear(self):
188
        pass
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
189
1551.2.29 by Aaron Bentley
Got stack handling under test
190
    def child_progress(self, **kwargs):
191
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
192
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
193
648 by Martin Pool
- import aaron's progress-indicator code
194
def str_tdelta(delt):
195
    if delt is None:
196
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
197
    delt = int(round(delt))
198
    return '%d:%02d:%02d' % (delt/3600,
199
                             (delt/60) % 60,
200
                             delt % 60)
201
202
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
203
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
204
    if start_time is None:
205
        return None
206
207
    if not total:
208
        return None
209
210
    if current < enough_samples:
211
        return None
212
213
    if current > total:
214
        return None                     # wtf?
215
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
216
    elapsed = time.time() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
217
218
    if elapsed < 2.0:                   # not enough time to estimate
219
        return None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
220
660 by Martin Pool
- use plain unix time, not datetime module
221
    total_duration = float(elapsed) * float(total) / float(current)
222
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
223
    if last_updates and len(last_updates) >= n_recent:
224
        avg = sum(last_updates) / float(len(last_updates))
225
        time_left = avg * (total - current)
226
227
        old_time_left = total_duration - elapsed
228
229
        # We could return the average, or some other value here
230
        return (time_left + old_time_left) / 2
231
660 by Martin Pool
- use plain unix time, not datetime module
232
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
233
649 by Martin Pool
- some cleanups for the progressbar method
234
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
235
class ProgressPhase(object):
236
    """Update progress object with the current phase"""
237
    def __init__(self, message, total, pb):
238
        object.__init__(self)
239
        self.pb = pb
240
        self.message = message
241
        self.total = total
242
        self.cur_phase = None
243
244
    def next_phase(self):
245
        if self.cur_phase is None:
246
            self.cur_phase = 0
247
        else:
248
            self.cur_phase += 1
249
        self.pb.update(self.message, self.cur_phase, self.total)