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