~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
6379.6.3 by Jelmer Vernooij
Use absolute_import.
17
from __future__ import absolute_import
649 by Martin Pool
- some cleanups for the progressbar method
18
3006.3.3 by Robert Collins
Docstring improvement and remove TODO's from progres.py.
19
"""Progress indicators.
20
21
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
22
will manage a conceptual stack of nested activities.
649 by Martin Pool
- some cleanups for the progressbar method
23
"""
24
934 by Martin Pool
todo
25
648 by Martin Pool
- import aaron's progress-indicator code
26
import sys
660 by Martin Pool
- use plain unix time, not datetime module
27
import time
964 by Martin Pool
- show progress on dumb terminals by printing dots
28
import os
649 by Martin Pool
- some cleanups for the progressbar method
29
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
30
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
31
from bzrlib.symbol_versioning import (
32
    deprecated_in,
4712.1.2 by Martin Pool
Fix missing import
33
    deprecated_method,
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
34
    )
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
35
36
649 by Martin Pool
- some cleanups for the progressbar method
37
def _supports_progress(f):
4449.3.1 by Martin Pool
Un-soft-deprecate _supports_progress - still useful
38
    """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
39
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
40
    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
41
    output, and that we can repaint a line to update it.
4449.3.1 by Martin Pool
Un-soft-deprecate _supports_progress - still useful
42
43
    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
44
    """
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
45
    isatty = getattr(f, 'isatty', None)
46
    if isatty is None:
695 by Martin Pool
- don't display progress bars on really dumb terminals
47
        return False
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
48
    if not isatty():
695 by Martin Pool
- don't display progress bars on really dumb terminals
49
        return False
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
50
    # The following case also handles Win32 - on that platform $TERM is
51
    # typically never set, so the case None is treated as a smart terminal,
52
    # not dumb.  <https://bugs.launchpad.net/bugs/334808>  win32 files do have
53
    # isatty methods that return true.
695 by Martin Pool
- don't display progress bars on really dumb terminals
54
    if os.environ.get('TERM') == 'dumb':
55
        # e.g. emacs compile window
56
        return False
57
    return True
649 by Martin Pool
- some cleanups for the progressbar method
58
59
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
60
class ProgressTask(object):
61
    """Model component of a progress indicator.
62
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
63
    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
64
    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
65
66
    Code updating the task may also set fields as hints about how to display
67
    it: show_pct, show_spinner, show_eta, show_count, show_bar.  UIs
68
    will not necessarily respect all these fields.
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
69
    
70
    :ivar update_latency: The interval (in seconds) at which the PB should be
71
        updated.  Setting this to zero suggests every update should be shown
72
        synchronously.
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
73
74
    :ivar show_transport_activity: If true (default), transport activity
75
        will be shown when this task is drawn.  Disable it if you're sure 
76
        that only irrelevant or uninteresting transport activity can occur
77
        during this task.
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
78
    """
79
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
80
    def __init__(self, parent_task=None, ui_factory=None, progress_view=None):
4110.2.13 by Martin Pool
doc
81
        """Construct a new progress task.
82
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
83
        :param parent_task: Enclosing ProgressTask or None.
84
85
        :param progress_view: ProgressView to display this ProgressTask.
86
87
        :param ui_factory: The UI factory that will display updates; 
88
            deprecated in favor of passing progress_view directly.
89
4110.2.13 by Martin Pool
doc
90
        Normally you should not call this directly but rather through
91
        `ui_factory.nested_progress_bar`.
92
        """
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
93
        self._parent_task = parent_task
94
        self._last_update = 0
95
        self.total_cnt = None
96
        self.current_cnt = None
97
        self.msg = ''
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
98
        # TODO: deprecate passing ui_factory
3882.8.2 by Martin Pool
ProgressTask holds a reference to the ui that displays it
99
        self.ui_factory = ui_factory
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
100
        self.progress_view = progress_view
3882.8.5 by Martin Pool
Progress tasks can indicate what kind of display is useful
101
        self.show_pct = False
102
        self.show_spinner = True
103
        self.show_eta = False,
104
        self.show_count = True
105
        self.show_bar = True
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
106
        self.update_latency = 0.1
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
107
        self.show_transport_activity = True
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
108
3948.2.1 by Martin Pool
Add ProgressTask repr
109
    def __repr__(self):
110
        return '%s(%r/%r, msg=%r)' % (
111
            self.__class__.__name__,
112
            self.current_cnt,
113
            self.total_cnt,
114
            self.msg)
115
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
116
    def update(self, msg, current_cnt=None, total_cnt=None):
117
        self.msg = msg
118
        self.current_cnt = current_cnt
119
        if total_cnt:
120
            self.total_cnt = total_cnt
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
121
        if self.progress_view:
122
            self.progress_view.show_progress(self)
123
        else:
124
            self.ui_factory._progress_updated(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
125
3882.8.8 by Martin Pool
Progress and UI test cleanups
126
    def tick(self):
127
        self.update(self.msg)
128
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
129
    def finished(self):
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
130
        if self.progress_view:
131
            self.progress_view.task_finished(self)
132
        else:
133
            self.ui_factory._progress_finished(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
134
135
    def make_sub_task(self):
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
136
        return ProgressTask(self, ui_factory=self.ui_factory,
137
            progress_view=self.progress_view)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
138
139
    def _overall_completion_fraction(self, child_fraction=0.0):
140
        """Return fractional completion of this task and its parents
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
141
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
142
        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()
143
        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
144
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
145
        else:
4110.2.17 by Martin Pool
If one ProgressTask has no count, it passes through that of its child
146
            # if this task has no estimation, it just passes on directly
147
            # whatever the child has measured...
148
            own_fraction = child_fraction
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
149
        if self._parent_task is None:
150
            return own_fraction
151
        else:
152
            if own_fraction is None:
153
                own_fraction = 0.0
154
            return self._parent_task._overall_completion_fraction(own_fraction)
155
156
    def clear(self):
4471.2.1 by Martin Pool
Comment on deprecating ProgressTask.clear
157
        # TODO: deprecate this method; the model object shouldn't be concerned
158
        # with whether it's shown or not.  Most callers use this because they
159
        # want to write some different non-progress output to the screen, but
160
        # they should probably instead use a stream that's synchronized with
161
        # the progress output.  It may be there is a model-level use for
162
        # saying "this task's not active at the moment" but I don't see it. --
163
        # mbp 20090623
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
164
        if self.progress_view:
165
            self.progress_view.clear()
166
        else:
167
            self.ui_factory.clear_term()
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
168
649 by Martin Pool
- some cleanups for the progressbar method
169
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
170
class DummyProgress(object):
1104 by Martin Pool
- Add a simple UIFactory
171
    """Progress-bar standin that does nothing.
172
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
173
    This was previously often constructed by application code if no progress
174
    bar was explicitly passed in.  That's no longer recommended: instead, just
175
    create a progress task from the ui_factory.  This class can be used in
176
    test code that needs to fake a progress task for some reason.
177
    """
3882.8.8 by Martin Pool
Progress and UI test cleanups
178
1104 by Martin Pool
- Add a simple UIFactory
179
    def tick(self):
180
        pass
181
182
    def update(self, msg=None, current=None, total=None):
183
        pass
184
1551.2.27 by Aaron Bentley
Got propogation under test
185
    def child_update(self, message, current, total):
186
        pass
187
1104 by Martin Pool
- Add a simple UIFactory
188
    def clear(self):
189
        pass
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
190
1551.2.29 by Aaron Bentley
Got stack handling under test
191
    def child_progress(self, **kwargs):
192
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
193
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
194
648 by Martin Pool
- import aaron's progress-indicator code
195
def str_tdelta(delt):
196
    if delt is None:
197
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
198
    delt = int(round(delt))
199
    return '%d:%02d:%02d' % (delt/3600,
200
                             (delt/60) % 60,
201
                             delt % 60)
202
203
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
204
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
205
    if start_time is None:
206
        return None
207
208
    if not total:
209
        return None
210
211
    if current < enough_samples:
212
        return None
213
214
    if current > total:
215
        return None                     # wtf?
216
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
217
    elapsed = time.time() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
218
219
    if elapsed < 2.0:                   # not enough time to estimate
220
        return None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
221
660 by Martin Pool
- use plain unix time, not datetime module
222
    total_duration = float(elapsed) * float(total) / float(current)
223
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
224
    if last_updates and len(last_updates) >= n_recent:
225
        avg = sum(last_updates) / float(len(last_updates))
226
        time_left = avg * (total - current)
227
228
        old_time_left = total_duration - elapsed
229
230
        # We could return the average, or some other value here
231
        return (time_left + old_time_left) / 2
232
660 by Martin Pool
- use plain unix time, not datetime module
233
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
234
649 by Martin Pool
- some cleanups for the progressbar method
235
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
236
class ProgressPhase(object):
237
    """Update progress object with the current phase"""
238
    def __init__(self, message, total, pb):
239
        object.__init__(self)
240
        self.pb = pb
241
        self.message = message
242
        self.total = total
243
        self.cur_phase = None
244
245
    def next_phase(self):
246
        if self.cur_phase is None:
247
            self.cur_phase = 0
248
        else:
249
            self.cur_phase += 1
250
        self.pb.update(self.message, self.cur_phase, self.total)