~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.
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
61
    
62
    :ivar update_latency: The interval (in seconds) at which the PB should be
63
        updated.  Setting this to zero suggests every update should be shown
64
        synchronously.
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
65
66
    :ivar show_transport_activity: If true (default), transport activity
67
        will be shown when this task is drawn.  Disable it if you're sure 
68
        that only irrelevant or uninteresting transport activity can occur
69
        during this task.
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
70
    """
71
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
72
    def __init__(self, parent_task=None, ui_factory=None, progress_view=None):
4110.2.13 by Martin Pool
doc
73
        """Construct a new progress task.
74
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
75
        :param parent_task: Enclosing ProgressTask or None.
76
77
        :param progress_view: ProgressView to display this ProgressTask.
78
79
        :param ui_factory: The UI factory that will display updates; 
80
            deprecated in favor of passing progress_view directly.
81
4110.2.13 by Martin Pool
doc
82
        Normally you should not call this directly but rather through
83
        `ui_factory.nested_progress_bar`.
84
        """
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
85
        self._parent_task = parent_task
86
        self._last_update = 0
87
        self.total_cnt = None
88
        self.current_cnt = None
89
        self.msg = ''
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
90
        # TODO: deprecate passing ui_factory
3882.8.2 by Martin Pool
ProgressTask holds a reference to the ui that displays it
91
        self.ui_factory = ui_factory
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
92
        self.progress_view = progress_view
3882.8.5 by Martin Pool
Progress tasks can indicate what kind of display is useful
93
        self.show_pct = False
94
        self.show_spinner = True
95
        self.show_eta = False,
96
        self.show_count = True
97
        self.show_bar = True
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
98
        self.update_latency = 0.1
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
99
        self.show_transport_activity = True
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
100
3948.2.1 by Martin Pool
Add ProgressTask repr
101
    def __repr__(self):
102
        return '%s(%r/%r, msg=%r)' % (
103
            self.__class__.__name__,
104
            self.current_cnt,
105
            self.total_cnt,
106
            self.msg)
107
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
108
    def update(self, msg, current_cnt=None, total_cnt=None):
109
        self.msg = msg
110
        self.current_cnt = current_cnt
111
        if total_cnt:
112
            self.total_cnt = total_cnt
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
113
        if self.progress_view:
114
            self.progress_view.show_progress(self)
115
        else:
116
            self.ui_factory._progress_updated(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
117
3882.8.8 by Martin Pool
Progress and UI test cleanups
118
    def tick(self):
119
        self.update(self.msg)
120
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
121
    def finished(self):
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
122
        if self.progress_view:
123
            self.progress_view.task_finished(self)
124
        else:
125
            self.ui_factory._progress_finished(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
126
127
    def make_sub_task(self):
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
128
        return ProgressTask(self, ui_factory=self.ui_factory,
129
            progress_view=self.progress_view)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
130
131
    def _overall_completion_fraction(self, child_fraction=0.0):
132
        """Return fractional completion of this task and its parents
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
133
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
134
        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()
135
        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
136
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
137
        else:
4110.2.17 by Martin Pool
If one ProgressTask has no count, it passes through that of its child
138
            # if this task has no estimation, it just passes on directly
139
            # whatever the child has measured...
140
            own_fraction = child_fraction
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
141
        if self._parent_task is None:
142
            return own_fraction
143
        else:
144
            if own_fraction is None:
145
                own_fraction = 0.0
146
            return self._parent_task._overall_completion_fraction(own_fraction)
147
148
    def clear(self):
4471.2.1 by Martin Pool
Comment on deprecating ProgressTask.clear
149
        # TODO: deprecate this method; the model object shouldn't be concerned
150
        # with whether it's shown or not.  Most callers use this because they
151
        # want to write some different non-progress output to the screen, but
152
        # they should probably instead use a stream that's synchronized with
153
        # the progress output.  It may be there is a model-level use for
154
        # saying "this task's not active at the moment" but I don't see it. --
155
        # mbp 20090623
4449.3.4 by Martin Pool
ProgressTask now talks to ProgressView; easier to test
156
        if self.progress_view:
157
            self.progress_view.clear()
158
        else:
159
            self.ui_factory.clear_term()
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
160
649 by Martin Pool
- some cleanups for the progressbar method
161
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
162
class DummyProgress(object):
1104 by Martin Pool
- Add a simple UIFactory
163
    """Progress-bar standin that does nothing.
164
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
165
    This was previously often constructed by application code if no progress
166
    bar was explicitly passed in.  That's no longer recommended: instead, just
167
    create a progress task from the ui_factory.  This class can be used in
168
    test code that needs to fake a progress task for some reason.
169
    """
3882.8.8 by Martin Pool
Progress and UI test cleanups
170
1104 by Martin Pool
- Add a simple UIFactory
171
    def tick(self):
172
        pass
173
174
    def update(self, msg=None, current=None, total=None):
175
        pass
176
1551.2.27 by Aaron Bentley
Got propogation under test
177
    def child_update(self, message, current, total):
178
        pass
179
1104 by Martin Pool
- Add a simple UIFactory
180
    def clear(self):
181
        pass
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
182
1551.2.29 by Aaron Bentley
Got stack handling under test
183
    def child_progress(self, **kwargs):
184
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
185
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
186
648 by Martin Pool
- import aaron's progress-indicator code
187
def str_tdelta(delt):
188
    if delt is None:
189
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
190
    delt = int(round(delt))
191
    return '%d:%02d:%02d' % (delt/3600,
192
                             (delt/60) % 60,
193
                             delt % 60)
194
195
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
196
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
197
    if start_time is None:
198
        return None
199
200
    if not total:
201
        return None
202
203
    if current < enough_samples:
204
        return None
205
206
    if current > total:
207
        return None                     # wtf?
208
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
209
    elapsed = time.time() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
210
211
    if elapsed < 2.0:                   # not enough time to estimate
212
        return None
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
213
660 by Martin Pool
- use plain unix time, not datetime module
214
    total_duration = float(elapsed) * float(total) / float(current)
215
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
216
    if last_updates and len(last_updates) >= n_recent:
217
        avg = sum(last_updates) / float(len(last_updates))
218
        time_left = avg * (total - current)
219
220
        old_time_left = total_duration - elapsed
221
222
        # We could return the average, or some other value here
223
        return (time_left + old_time_left) / 2
224
660 by Martin Pool
- use plain unix time, not datetime module
225
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
226
649 by Martin Pool
- some cleanups for the progressbar method
227
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
228
class ProgressPhase(object):
229
    """Update progress object with the current phase"""
230
    def __init__(self, message, total, pb):
231
        object.__init__(self)
232
        self.pb = pb
233
        self.message = message
234
        self.total = total
235
        self.cur_phase = None
236
237
    def next_phase(self):
238
        if self.cur_phase is None:
239
            self.cur_phase = 0
240
        else:
241
            self.cur_phase += 1
242
        self.pb.update(self.message, self.cur_phase, self.total)