/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
1
# Copyright (C) 2005, 2008 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.49.21 by John Arbash Meinel
Refactored bzrlib/ui.py into a module with the possibility for multiple ui forms.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.49.21 by John Arbash Meinel
Refactored bzrlib/ui.py into a module with the possibility for multiple ui forms.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.49.21 by John Arbash Meinel
Refactored bzrlib/ui.py into a module with the possibility for multiple ui forms.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
19
"""Text UI, write output to the console.
20
"""
21
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
22
import sys
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
23
import time
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
24
25
from bzrlib.lazy_import import lazy_import
26
lazy_import(globals(), """
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
27
import getpass
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
28
29
from bzrlib import (
30
    progress,
2294.4.1 by Vincent Ladeuil
Add a UIFactory.get_login method, fix tests.
31
    osutils,
3882.8.8 by Martin Pool
Progress and UI test cleanups
32
    symbol_versioning,
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
33
    )
3882.8.8 by Martin Pool
Progress and UI test cleanups
34
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
35
""")
36
1687.1.4 by Robert Collins
Add bzrlib.ui.ui_factory.get_boolean().
37
from bzrlib.ui import CLIUIFactory
38
39
40
class TextUIFactory(CLIUIFactory):
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
41
    """A UI factory for Text user interefaces."""
42
1692.3.3 by Robert Collins
Get run_bzr in tests to always assign a new, clean ui factory.
43
    def __init__(self,
44
                 bar_type=None,
3882.8.11 by Martin Pool
Choose the UIFactory class depending on the terminal capabilities
45
                 stdin=None,
1692.3.3 by Robert Collins
Get run_bzr in tests to always assign a new, clean ui factory.
46
                 stdout=None,
47
                 stderr=None):
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
48
        """Create a TextUIFactory.
49
50
        :param bar_type: The type of progress bar to create. It defaults to 
51
                         letting the bzrlib.progress.ProgressBar factory auto
3882.8.3 by Martin Pool
Move display of transport throughput into TextProgressView
52
                         select.   Deprecated.
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
53
        """
3882.8.11 by Martin Pool
Choose the UIFactory class depending on the terminal capabilities
54
        super(TextUIFactory, self).__init__(stdin=stdin,
55
                stdout=stdout, stderr=stderr)
3882.8.6 by Martin Pool
TextUIFactory ignores and deprecates the bar_type parameter
56
        if bar_type:
57
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 11, 0))
58
                % "bar_type parameter")
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
59
        # paints progress, network activity, etc
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
60
        self._progress_view = TextProgressView(self.stderr)
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
61
1687.1.4 by Robert Collins
Add bzrlib.ui.ui_factory.get_boolean().
62
    def prompt(self, prompt):
63
        """Emit prompt on the CLI."""
2294.4.1 by Vincent Ladeuil
Add a UIFactory.get_login method, fix tests.
64
        self.stdout.write(prompt)
1687.1.4 by Robert Collins
Add bzrlib.ui.ui_factory.get_boolean().
65
        
1558.8.1 by Aaron Bentley
Fix overall progress bar's interaction with 'note' and 'warning'
66
    def clear_term(self):
67
        """Prepare the terminal for output.
68
69
        This will, clear any progress bars, and leave the cursor at the
70
        leftmost position."""
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
71
        # XXX: If this is preparing to write to stdout, but that's for example
72
        # directed into a file rather than to the terminal, and the progress
73
        # bar _is_ going to the terminal, we shouldn't need
74
        # to clear it.  We might need to separately check for the case of 
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
75
        self._progress_view.clear()
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
76
3882.8.4 by Martin Pool
All UI factories should support note()
77
    def note(self, msg):
78
        """Write an already-formatted message, clearing the progress bar if necessary."""
79
        self.clear_term()
80
        self.stdout.write(msg + '\n')
81
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
82
    def report_transport_activity(self, transport, byte_count, direction):
83
        """Called by transports as they do IO.
84
        
85
        This may update a progress bar, spinner, or similar display.
86
        By default it does nothing.
87
        """
3882.8.3 by Martin Pool
Move display of transport throughput into TextProgressView
88
        self._progress_view.show_transport_activity(byte_count)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
89
90
    def show_progress(self, task):
91
        """A task has been updated and wants to be displayed.
92
        """
93
        self._progress_view.show_progress(task)
94
95
    def progress_finished(self, task):
96
        CLIUIFactory.progress_finished(self, task)
97
        if not self._task_stack:
98
            # finished top-level task
99
            self._progress_view.clear()
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
100
101
102
class TextProgressView(object):
103
    """Display of progress bar and other information on a tty.
104
    
105
    This shows one line of text, including possibly a network indicator, spinner, 
106
    progress bar, message, etc.
107
108
    One instance of this is created and held by the UI, and fed updates when a
109
    task wants to be painted.
110
111
    Transports feed data to this through the ui_factory object.
112
    """
113
114
    def __init__(self, term_file):
115
        self._term_file = term_file
116
        # true when there's output on the screen we may need to clear
117
        self._have_output = False
118
        # XXX: We could listen for SIGWINCH and update the terminal width...
119
        self._width = osutils.terminal_width()
120
        self._last_transport_msg = ''
121
        self._spin_pos = 0
122
        # time we last repainted the screen
123
        self._last_repaint = 0
124
        # time we last got information about transport activity
125
        self._transport_update_time = 0
126
        self._task_fraction = None
127
        self._last_task = None
128
        self._total_byte_count = 0
129
        self._bytes_since_update = 0
130
131
    def _show_line(self, s):
132
        n = self._width - 1
133
        self._term_file.write('\r%-*.*s\r' % (n, n, s))
134
135
    def clear(self):
136
        if self._have_output:
137
            self._show_line('')
138
        self._have_output = False
139
140
    def _render_bar(self):
141
        # return a string for the progress bar itself
142
        if (self._last_task is not None) and self._last_task.show_bar:
143
            spin_str =  r'/-\|'[self._spin_pos % 4]
144
            self._spin_pos += 1
145
            f = self._task_fraction or 0
146
            cols = 20
147
            # number of markers highlighted in bar
148
            markers = int(round(float(cols) * f)) - 1
149
            bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
150
            return bar_str
151
        elif (self._last_task is None) or self._last_task.show_spinner:
152
            spin_str =  r'/-\|'[self._spin_pos % 4]
153
            self._spin_pos += 1
154
            return spin_str + ' '
155
        else:
156
            return ''
157
158
    def _format_task(self, task):
159
        if not task.show_count:
160
            s = ''
161
        elif task.total_cnt is not None:
162
            s = ' %d/%d' % (task.current_cnt, task.total_cnt)
163
        elif task.current_cnt is not None:
164
            s = ' %d' % (task.current_cnt)
165
        else:
166
            s = ''
167
        self._task_fraction = task._overall_completion_fraction()
168
        # compose all the parent messages
169
        t = task
170
        m = task.msg
171
        while t._parent_task:
172
            t = t._parent_task
173
            if t.msg:
174
                m = t.msg + ':' + m
175
        return m + s
176
177
    def _repaint(self):
178
        bar_string = self._render_bar()
179
        if self._last_task:
180
            task_msg = self._format_task(self._last_task)
181
        else:
182
            task_msg = ''
183
        trans = self._last_transport_msg
184
        if trans and task_msg:
185
            trans += ' | '
186
        s = (bar_string
187
             + trans
188
             + task_msg
189
             )
190
        self._show_line(s)
191
        self._have_output = True
192
193
    def show_progress(self, task):
194
        self._last_task = task
195
        now = time.time()
196
        if now < self._last_repaint + 0.1:
197
            return
198
        if now > self._transport_update_time + 5:
199
            # no recent activity; expire it
200
            self._last_transport_msg = ''
201
        self._last_repaint = now
202
        self._repaint()
203
204
    def show_transport_activity(self, byte_count):
205
        """Called by transports as they do IO.
206
        
207
        This may update a progress bar, spinner, or similar display.
208
        By default it does nothing.
209
        """
210
        # XXX: Probably there should be a transport activity model, and that
211
        # too should be seen by the progress view, rather than being poked in
212
        # here.
213
        self._total_byte_count += byte_count
214
        self._bytes_since_update += byte_count
215
        now = time.time()
216
        if self._transport_update_time is None:
217
            self._transport_update_time = now
218
        elif now >= (self._transport_update_time + 0.2):
219
            # guard against clock stepping backwards, and don't update too
220
            # often
221
            rate = self._bytes_since_update / (now - self._transport_update_time)
222
            msg = ("%6dkB @ %4dkB/s" %
223
                (self._total_byte_count>>10, int(rate)>>10,))
224
            self._transport_update_time = now
225
            self._last_repaint = now
226
            self._bytes_since_update = 0
227
            self._last_transport_msg = msg
228
            self._repaint()
229
230