/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5752.3.8 by John Arbash Meinel
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts
1
# Copyright (C) 2005-2011 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.49.21 by John Arbash Meinel
Refactored bzrlib/ui.py into a module with the possibility for multiple ui forms.
16
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
17
"""Text UI, write output to the console."""
1185.49.21 by John Arbash Meinel
Refactored bzrlib/ui.py into a module with the possibility for multiple ui forms.
18
6379.6.1 by Jelmer Vernooij
Import absolute_import in a few places.
19
from __future__ import absolute_import
20
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
21
import codecs
4449.2.1 by Martin Pool
TextUIFactory now respects BZR_PROGRESS_BAR again
22
import os
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
23
import sys
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
24
import warnings
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
25
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
26
from ..lazy_import import lazy_import
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
27
lazy_import(globals(), """
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
28
import getpass
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
29
import time
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
30
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
31
from breezy import (
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
32
    debug,
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
33
    progress,
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
34
    )
35
""")
36
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
37
from .. import (
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
38
    config,
2294.4.1 by Vincent Ladeuil
Add a UIFactory.get_login method, fix tests.
39
    osutils,
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
40
    trace,
1996.3.27 by John Arbash Meinel
lazy import getpass in bzrlib.ui.text
41
    )
6695.1.1 by Martin
Make ui package pass tests on Python 3
42
from ..sixish import (
43
    text_type,
44
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
45
from . import (
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
46
    NullProgressView,
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
47
    UIFactory,
4449.3.15 by Martin Pool
Move NullProgressView and make_progress_view up to UIFactory base class
48
    )
1687.1.4 by Robert Collins
Add bzrlib.ui.ui_factory.get_boolean().
49
50
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
51
class _ChooseUI(object):
52
53
    """ Helper class for choose implementation.
54
    """
55
56
    def __init__(self, ui, msg, choices, default):
57
        self.ui = ui
58
        self._setup_mode()
59
        self._build_alternatives(msg, choices, default)
60
61
    def _setup_mode(self):
62
        """Setup input mode (line-based, char-based) and echo-back.
63
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
64
        Line-based input is used if the BRZ_TEXTUI_INPUT environment
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
65
        variable is set to 'line-based', or if there is no controlling
66
        terminal.
67
        """
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
68
        is_tty = self.ui.raw_stdin.isatty()
69
        if (os.environ.get('BRZ_TEXTUI_INPUT') != 'line-based' and
70
                self.ui.raw_stdin == sys.stdin and is_tty):
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
71
            self.line_based = False
72
            self.echo_back = True
73
        else:
74
            self.line_based = True
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
75
            self.echo_back = not is_tty
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
76
77
    def _build_alternatives(self, msg, choices, default):
78
        """Parse choices string.
79
80
        Setup final prompt and the lists of choices and associated
81
        shortcuts.
82
        """
83
        index = 0
84
        help_list = []
85
        self.alternatives = {}
86
        choices = choices.split('\n')
87
        if default is not None and default not in range(0, len(choices)):
88
            raise ValueError("invalid default index")
89
        for c in choices:
90
            name = c.replace('&', '').lower()
91
            choice = (name, index)
92
            if name in self.alternatives:
93
                raise ValueError("duplicated choice: %s" % name)
94
            self.alternatives[name] = choice
95
            shortcut = c.find('&')
96
            if -1 != shortcut and (shortcut + 1) < len(c):
97
                help = c[:shortcut]
98
                help += '[' + c[shortcut + 1] + ']'
99
                help += c[(shortcut + 2):]
100
                shortcut = c[shortcut + 1]
101
            else:
102
                c = c.replace('&', '')
103
                shortcut = c[0]
104
                help = '[%s]%s' % (shortcut, c[1:])
105
            shortcut = shortcut.lower()
106
            if shortcut in self.alternatives:
107
                raise ValueError("duplicated shortcut: %s" % shortcut)
108
            self.alternatives[shortcut] = choice
109
            # Add redirections for default.
110
            if index == default:
111
                self.alternatives[''] = choice
112
                self.alternatives['\r'] = choice
113
            help_list.append(help)
114
            index += 1
115
116
        self.prompt = u'%s (%s): ' % (msg, ', '.join(help_list))
117
118
    def _getline(self):
119
        line = self.ui.stdin.readline()
120
        if '' == line:
121
            raise EOFError
122
        return line.strip()
123
124
    def _getchar(self):
125
        char = osutils.getchar()
126
        if char == chr(3): # INTR
127
            raise KeyboardInterrupt
128
        if char == chr(4): # EOF (^d, C-d)
129
            raise EOFError
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
130
        if isinstance(char, bytes):
131
            return char.decode('ascii', 'replace')
132
        return char
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
133
134
    def interact(self):
135
        """Keep asking the user until a valid choice is made.
136
        """
137
        if self.line_based:
138
            getchoice = self._getline
139
        else:
140
            getchoice = self._getchar
141
        iter = 0
142
        while True:
143
            iter += 1
144
            if 1 == iter or self.line_based:
145
                self.ui.prompt(self.prompt)
146
            try:
147
                choice = getchoice()
148
            except EOFError:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
149
                self.ui.stderr.write(u'\n')
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
150
                return None
151
            except KeyboardInterrupt:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
152
                self.ui.stderr.write(u'\n')
153
                raise
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
154
            choice = choice.lower()
155
            if choice not in self.alternatives:
156
                # Not a valid choice, keep on asking.
157
                continue
158
            name, index = self.alternatives[choice]
159
            if self.echo_back:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
160
                self.ui.stderr.write(name + u'\n')
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
161
            return index
162
163
6561.2.1 by Vincent Ladeuil
Add a ``progress_bar`` config option.
164
opt_progress_bar = config.Option(
165
    'progress_bar', help='Progress bar type.',
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
166
    default_from_env=['BRZ_PROGRESS_BAR'], default=None,
6561.2.1 by Vincent Ladeuil
Add a ``progress_bar`` config option.
167
    invalid='error')
168
169
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
170
class TextUIFactory(UIFactory):
6561.2.1 by Vincent Ladeuil
Add a ``progress_bar`` config option.
171
    """A UI factory for Text user interfaces."""
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
172
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
173
    def __init__(self, stdin, stdout, stderr):
174
        """Create a TextUIFactory."""
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
175
        super(TextUIFactory, self).__init__()
176
        self.stdin = stdin
177
        self.stdout = stdout
178
        self.stderr = stderr
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
179
        self._progress_view = NullProgressView()
180
181
    def __enter__(self):
182
        # Choose default encoding and handle py2/3 differences
183
        self._setup_streams()
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
184
        # paints progress, network activity, etc
4449.3.15 by Martin Pool
Move NullProgressView and make_progress_view up to UIFactory base class
185
        self._progress_view = self.make_progress_view()
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
186
        return self
187
188
    def _setup_streams(self):
189
        self.raw_stdin = _unwrap_stream(self.stdin)
190
        self.stdin = _wrap_in_stream(self.raw_stdin)
191
        self.raw_stdout = _unwrap_stream(self.stdout)
192
        self.stdout = _wrap_out_stream(self.raw_stdout)
193
        self.raw_stderr = _unwrap_stream(self.stderr)
194
        self.stderr = _wrap_out_stream(self.raw_stderr)
4797.20.2 by
Register SIGWINCH only when creating a TextUIFactory
195
6182.2.13 by Benoît Pierre
Rename ui.confirm to ui.choose.
196
    def choose(self, msg, choices, default=None):
6182.2.11 by Benoît Pierre
Small tweak to TextUIFactory.confirm documentation.
197
        """Prompt the user for a list of alternatives.
6182.2.2 by Benoît Pierre
Implement TextUIFactory.confirm.
198
6182.2.14 by Benoît Pierre
Rework TextUIFactory.choose again to make the code simpler to follow.
199
        Support both line-based and char-based editing.
6182.2.2 by Benoît Pierre
Implement TextUIFactory.confirm.
200
201
        In line-based mode, both the shortcut and full choice name are valid
6182.2.13 by Benoît Pierre
Rename ui.confirm to ui.choose.
202
        answers, e.g. for choose('prompt', '&yes\n&no'): 'y', ' Y ', ' yes',
6182.2.2 by Benoît Pierre
Implement TextUIFactory.confirm.
203
        'YES ' are all valid input lines for choosing 'yes'.
204
205
        An empty line, when in line-based mode, or pressing enter in char-based
206
        mode will select the default choice (if any).
207
208
        Choice is echoed back if:
209
        - input is char-based; which means a controlling terminal is available,
210
          and osutils.getchar is used
211
        - input is line-based, and no controlling terminal is available
212
        """
6182.2.14 by Benoît Pierre
Rework TextUIFactory.choose again to make the code simpler to follow.
213
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
214
        choose_ui = _ChooseUI(self, msg, choices, default)
6182.2.14 by Benoît Pierre
Rework TextUIFactory.choose again to make the code simpler to follow.
215
        return choose_ui.interact()
6182.2.2 by Benoît Pierre
Implement TextUIFactory.confirm.
216
4961.1.2 by Martin Pool
quietness-state is now tracked on UIFactory
217
    def be_quiet(self, state):
218
        if state and not self._quiet:
219
            self.clear_term()
220
        UIFactory.be_quiet(self, state)
4961.1.3 by Martin Pool
trace quietness now controls whether the progress bar appears
221
        self._progress_view = self.make_progress_view()
4961.1.2 by Martin Pool
quietness-state is now tracked on UIFactory
222
1558.8.1 by Aaron Bentley
Fix overall progress bar's interaction with 'note' and 'warning'
223
    def clear_term(self):
224
        """Prepare the terminal for output.
225
226
        This will, clear any progress bars, and leave the cursor at the
227
        leftmost position."""
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
228
        # XXX: If this is preparing to write to stdout, but that's for example
229
        # directed into a file rather than to the terminal, and the progress
230
        # bar _is_ going to the terminal, we shouldn't need
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
231
        # 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
232
        self._progress_view.clear()
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
233
4597.3.37 by Vincent Ladeuil
Allows ui factories to query users for an integer.
234
    def get_integer(self, prompt):
235
        while True:
236
            self.prompt(prompt)
237
            line = self.stdin.readline()
238
            try:
239
                return int(line)
240
            except ValueError:
241
                pass
242
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
243
    def get_non_echoed_password(self):
244
        isatty = getattr(self.stdin, 'isatty', None)
245
        if isatty is not None and isatty():
246
            # getpass() ensure the password is not echoed and other
247
            # cross-platform niceties
248
            password = getpass.getpass('')
249
        else:
250
            # echo doesn't make sense without a terminal
251
            password = self.stdin.readline()
252
            if not password:
253
                password = None
6559.2.1 by Vincent Ladeuil
Makes AuthenticationConfig always return unicode user names and passwords.
254
            else:
255
                if password[-1] == '\n':
256
                    password = password[:-1]
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
257
        return password
258
5863.6.1 by Jelmer Vernooij
Require a unicode prompt to be passed into all methods that prompt.
259
    def get_password(self, prompt=u'', **kwargs):
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
260
        """Prompt the user for a password.
261
262
        :param prompt: The prompt to present the user
263
        :param kwargs: Arguments which will be expanded into the prompt.
264
                       This lets front ends display different things if
265
                       they so choose.
266
        :return: The password string, return None if the user
267
                 canceled the request.
268
        """
269
        prompt += ': '
270
        self.prompt(prompt, **kwargs)
271
        # There's currently no way to say 'i decline to enter a password'
272
        # as opposed to 'my password is empty' -- does it matter?
273
        return self.get_non_echoed_password()
274
275
    def get_username(self, prompt, **kwargs):
276
        """Prompt the user for a username.
277
278
        :param prompt: The prompt to present the user
279
        :param kwargs: Arguments which will be expanded into the prompt.
280
                       This lets front ends display different things if
281
                       they so choose.
282
        :return: The username string, return None if the user
283
                 canceled the request.
284
        """
285
        prompt += ': '
286
        self.prompt(prompt, **kwargs)
287
        username = self.stdin.readline()
288
        if not username:
289
            username = None
6559.2.1 by Vincent Ladeuil
Makes AuthenticationConfig always return unicode user names and passwords.
290
        else:
291
            if username[-1] == '\n':
292
                username = username[:-1]
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
293
        return username
294
4449.3.15 by Martin Pool
Move NullProgressView and make_progress_view up to UIFactory base class
295
    def make_progress_view(self):
296
        """Construct and return a new ProgressView subclass for this UI.
297
        """
4961.1.2 by Martin Pool
quietness-state is now tracked on UIFactory
298
        # with --quiet, never any progress view
5243.1.2 by Martin
Point launchpad links in comments at production server rather than edge
299
        # <https://bugs.launchpad.net/bzr/+bug/320035>.  Otherwise if the
4961.1.2 by Martin Pool
quietness-state is now tracked on UIFactory
300
        # user specifically requests either text or no progress bars, always
301
        # do that.  otherwise, guess based on $TERM and tty presence.
302
        if self.is_quiet():
303
            return NullProgressView()
6561.2.1 by Vincent Ladeuil
Add a ``progress_bar`` config option.
304
        pb_type = config.GlobalStack().get('progress_bar')
305
        if pb_type == 'none': # Explicit requirement
306
            return NullProgressView()
307
        if (pb_type == 'text' # Explicit requirement
308
            or progress._supports_progress(self.stderr)): # Guess
309
            return TextProgressView(self.stderr)
310
        # No explicit requirement and no successful guess
311
        return NullProgressView()
4449.2.1 by Martin Pool
TextUIFactory now respects BZR_PROGRESS_BAR again
312
4792.8.5 by Martin Pool
Support encoding_type=exact for make_output_stream
313
    def _make_output_stream_explicit(self, encoding, encoding_type):
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
314
        return TextUIOutputStream(self, self.stdout, encoding, encoding_type)
4792.8.2 by Martin Pool
New method ui_factory.make_output_stream
315
3882.8.4 by Martin Pool
All UI factories should support note()
316
    def note(self, msg):
317
        """Write an already-formatted message, clearing the progress bar if necessary."""
318
        self.clear_term()
319
        self.stdout.write(msg + '\n')
320
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
321
    def prompt(self, prompt, **kwargs):
322
        """Emit prompt on the CLI.
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
323
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
324
        :param kwargs: Dictionary of arguments to insert into the prompt,
325
            to allow UIs to reformat the prompt.
326
        """
6695.1.1 by Martin
Make ui package pass tests on Python 3
327
        if not isinstance(prompt, text_type):
5863.6.1 by Jelmer Vernooij
Require a unicode prompt to be passed into all methods that prompt.
328
            raise ValueError("prompt %r not a unicode string" % prompt)
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
329
        if kwargs:
330
            # See <https://launchpad.net/bugs/365891>
331
            prompt = prompt % kwargs
332
        self.clear_term()
6182.2.10 by Benoît Pierre
Flush stdout before prompting in TextUIFactory.
333
        self.stdout.flush()
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
334
        self.stderr.write(prompt)
335
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
336
    def report_transport_activity(self, transport, byte_count, direction):
337
        """Called by transports as they do IO.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
338
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
339
        This may update a progress bar, spinner, or similar display.
340
        By default it does nothing.
341
        """
4449.2.1 by Martin Pool
TextUIFactory now respects BZR_PROGRESS_BAR again
342
        self._progress_view.show_transport_activity(transport,
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
343
            direction, byte_count)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
344
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
345
    def log_transport_activity(self, display=False):
346
        """See UIFactory.log_transport_activity()"""
347
        log = getattr(self._progress_view, 'log_transport_activity', None)
348
        if log is not None:
349
            log(display=display)
350
4711.1.7 by Martin Pool
Add UIFactory.show_error, show_warning, show_message
351
    def show_error(self, msg):
352
        self.clear_term()
353
        self.stderr.write("bzr: error: %s\n" % msg)
354
4711.1.8 by Martin Pool
Add show_warning and show_message tests and implementations
355
    def show_message(self, msg):
356
        self.note(msg)
357
358
    def show_warning(self, msg):
359
        self.clear_term()
360
        self.stderr.write("bzr: warning: %s\n" % msg)
361
3948.2.3 by Martin Pool
Make the interface from ProgressTask to ui more private
362
    def _progress_updated(self, task):
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
363
        """A task has been updated and wants to be displayed.
364
        """
4070.1.1 by Martin Pool
Be more robust about pb updates when none are active
365
        if not self._task_stack:
366
            warnings.warn("%r updated but no tasks are active" %
367
                (task,))
368
        elif task != self._task_stack[-1]:
4961.2.19 by Martin Pool
Suppress un-helpful warning about progress task ordering
369
            # We used to check it was the top task, but it's hard to always
370
            # get this right and it's not necessarily useful: any actual
371
            # problems will be evident in use
372
            #warnings.warn("%r is not the top progress task %r" %
373
            #     (task, self._task_stack[-1]))
374
            pass
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
375
        self._progress_view.show_progress(task)
376
3948.2.5 by Martin Pool
rename to _progress_all_finished
377
    def _progress_all_finished(self):
3948.2.3 by Martin Pool
Make the interface from ProgressTask to ui more private
378
        self._progress_view.clear()
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
379
4634.144.8 by Martin Pool
Generalize to ui_factory.show_user_warning
380
    def show_user_warning(self, warning_id, **message_args):
4634.144.5 by Martin Pool
Cleaner presentation and tests for warn_cross_format_fetch
381
        """Show a text message to the user.
382
383
        Explicitly not for warnings about bzr apis, deprecations or internals.
384
        """
385
        # eventually trace.warning should migrate here, to avoid logging and
386
        # be easier to test; that has a lot of test fallout so for now just
387
        # new code can call this
4634.144.11 by Martin Pool
Rename squelched to suppressed
388
        if warning_id not in self.suppressed_warnings:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
389
            warning = self.format_user_warning(warning_id, message_args)
390
            self.stderr.write(warning + '\n')
391
392
393
def pad_to_width(line, width, encoding_hint='ascii'):
394
    """Truncate or pad unicode line to width.
395
396
    This is best-effort for now, and strings containing control codes or
397
    non-ascii text may be cut and padded incorrectly.
398
    """
399
    s = line.encode(encoding_hint, 'replace')
6621.2.26 by Martin
Misc set of changes to get started with selftest on Python 3
400
    return (b'%-*.*s' % (width, width, s)).decode(encoding_hint)
4634.144.5 by Martin Pool
Cleaner presentation and tests for warn_cross_format_fetch
401
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
402
403
class TextProgressView(object):
404
    """Display of progress bar and other information on a tty.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
405
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
406
    This shows one line of text, including possibly a network indicator,
407
    spinner, progress bar, message, etc.
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
408
409
    One instance of this is created and held by the UI, and fed updates when a
410
    task wants to be painted.
411
412
    Transports feed data to this through the ui_factory object.
3948.2.2 by Martin Pool
Corrections to finishing progress bars
413
414
    The Progress views can comprise a tree with _parent_task pointers, but
415
    this only prints the stack from the nominated current task up to the root.
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
416
    """
417
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
418
    def __init__(self, term_file, encoding=None, errors=None):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
419
        self._term_file = term_file
6437.57.3 by Martin Packman
Make TextProgressView somewhat aware of encodings
420
        if encoding is None:
6437.57.7 by Martin Packman
Correct and test fallback to ascii logic when a stream has no encoding
421
            self._encoding = getattr(term_file, "encoding", None) or "ascii"
6437.57.3 by Martin Packman
Make TextProgressView somewhat aware of encodings
422
        else:
423
            self._encoding = encoding
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
424
        # true when there's output on the screen we may need to clear
425
        self._have_output = False
426
        self._last_transport_msg = ''
427
        self._spin_pos = 0
428
        # time we last repainted the screen
429
        self._last_repaint = 0
430
        # time we last got information about transport activity
431
        self._transport_update_time = 0
432
        self._last_task = None
433
        self._total_byte_count = 0
434
        self._bytes_since_update = 0
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
435
        self._bytes_by_direction = {'unknown': 0, 'read': 0, 'write': 0}
4906.1.5 by John Arbash Meinel
Include the KiB/s for the transfer.
436
        self._first_byte_time = None
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
437
        self._fraction = 0
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
438
        # force the progress bar to be off, as at the moment it doesn't
4880.2.1 by Martin Pool
Text progress view is now only a spinner not a bar.
439
        # correspond reliably to overall command progress
440
        self.enable_bar = False
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
441
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
442
    def _avail_width(self):
443
        # we need one extra space for terminals that wrap on last char
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
444
        w = osutils.terminal_width()
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
445
        if w is None:
5050.16.1 by Martin Pool
Clear off progress bars by painting spaces.
446
            return None
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
447
        else:
448
            return w - 1
449
6437.57.3 by Martin Packman
Make TextProgressView somewhat aware of encodings
450
    def _show_line(self, u):
5050.16.1 by Martin Pool
Clear off progress bars by painting spaces.
451
        width = self._avail_width()
452
        if width is not None:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
453
            u = pad_to_width(u, width, encoding_hint=self._encoding)
454
        self._term_file.write('\r' + u + '\r')
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
455
456
    def clear(self):
457
        if self._have_output:
458
            self._show_line('')
459
        self._have_output = False
460
461
    def _render_bar(self):
462
        # return a string for the progress bar itself
4880.2.1 by Martin Pool
Text progress view is now only a spinner not a bar.
463
        if self.enable_bar and (
464
            (self._last_task is None) or self._last_task.show_bar):
4103.3.3 by Martin Pool
Show the progress bar part when showing activity by default
465
            # If there's no task object, we show space for the bar anyhow.
466
            # That's because most invocations of bzr will end showing progress
467
            # at some point, though perhaps only after doing some initial IO.
468
            # It looks better to draw the progress bar initially rather than
469
            # to have what looks like an incomplete progress bar.
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
470
            spin_str =  r'/-\|'[self._spin_pos % 4]
471
            self._spin_pos += 1
472
            cols = 20
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
473
            if self._last_task is None:
474
                completion_fraction = 0
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
475
                self._fraction = 0
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
476
            else:
477
                completion_fraction = \
478
                    self._last_task._overall_completion_fraction() or 0
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
479
            if (completion_fraction < self._fraction and 'progress' in
480
                debug.debug_flags):
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
481
                debug.set_trace()
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
482
            self._fraction = completion_fraction
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
483
            markers = int(round(float(cols) * completion_fraction)) - 1
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
484
            bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
485
            return bar_str
4912.1.1 by Martin Pool
Transport activity indicator is now shown even if there's no pb
486
        elif (self._last_task is None) or self._last_task.show_spinner:
4103.3.3 by Martin Pool
Show the progress bar part when showing activity by default
487
            # The last task wanted just a spinner, no bar
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
488
            spin_str =  r'/-\|'[self._spin_pos % 4]
489
            self._spin_pos += 1
490
            return spin_str + ' '
491
        else:
492
            return ''
493
494
    def _format_task(self, task):
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
495
        """Format task-specific parts of progress bar.
496
497
        :returns: (text_part, counter_part) both unicode strings.
498
        """
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
499
        if not task.show_count:
500
            s = ''
4017.1.1 by John Arbash Meinel
Get a pb.tick() to work after calling pb.update()
501
        elif task.current_cnt is not None and task.total_cnt is not None:
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
502
            s = ' %d/%d' % (task.current_cnt, task.total_cnt)
503
        elif task.current_cnt is not None:
504
            s = ' %d' % (task.current_cnt)
505
        else:
506
            s = ''
507
        # compose all the parent messages
508
        t = task
509
        m = task.msg
510
        while t._parent_task:
511
            t = t._parent_task
512
            if t.msg:
513
                m = t.msg + ':' + m
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
514
        return m, s
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
515
4110.2.16 by Martin Pool
Refactor TextProgressView a bit and add another test
516
    def _render_line(self):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
517
        bar_string = self._render_bar()
518
        if self._last_task:
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
519
            task_part, counter_part = self._format_task(self._last_task)
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
520
        else:
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
521
            task_part = counter_part = ''
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
522
        if self._last_task and not self._last_task.show_transport_activity:
523
            trans = ''
524
        else:
525
            trans = self._last_transport_msg
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
526
        # the bar separates the transport activity from the message, so even
527
        # if there's no bar or spinner, we must show something if both those
528
        # fields are present
529
        if (task_part or trans) and not bar_string:
530
            bar_string = '| '
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
531
        # preferentially truncate the task message if we don't have enough
532
        # space
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
533
        avail_width = self._avail_width()
534
        if avail_width is not None:
535
            # if terminal avail_width is unknown, don't truncate
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
536
            current_len = len(bar_string) + len(trans) + len(task_part) + len(counter_part)
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
537
            # GZ 2017-04-22: Should measure and truncate task_part properly
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
538
            gap = current_len - avail_width
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
539
            if gap > 0:
540
                task_part = task_part[:-gap-2] + '..'
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
541
        s = trans + bar_string + task_part + counter_part
542
        if avail_width is not None:
543
            if len(s) < avail_width:
544
                s = s.ljust(avail_width)
545
            elif len(s) > avail_width:
546
                s = s[:avail_width]
547
        return s
4110.2.16 by Martin Pool
Refactor TextProgressView a bit and add another test
548
549
    def _repaint(self):
550
        s = self._render_line()
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
551
        self._show_line(s)
552
        self._have_output = True
553
554
    def show_progress(self, task):
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
555
        """Called by the task object when it has changed.
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
556
557
        :param task: The top task object; its parents are also included
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
558
            by following links.
559
        """
4110.2.18 by Martin Pool
Progress bars always repaint when task structure is changed
560
        must_update = task is not self._last_task
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
561
        self._last_task = task
562
        now = time.time()
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
563
        if (not must_update) and (now < self._last_repaint + task.update_latency):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
564
            return
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
565
        if now > self._transport_update_time + 10:
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
566
            # no recent activity; expire it
567
            self._last_transport_msg = ''
568
        self._last_repaint = now
569
        self._repaint()
570
4449.2.1 by Martin Pool
TextUIFactory now respects BZR_PROGRESS_BAR again
571
    def show_transport_activity(self, transport, direction, byte_count):
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
572
        """Called by transports via the ui_factory, as they do IO.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
573
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
574
        This may update a progress bar, spinner, or similar display.
575
        By default it does nothing.
576
        """
4906.1.8 by John Arbash Meinel
Merge bzr.dev, resolve conflicts.
577
        # XXX: there should be a transport activity model, and that too should
578
        #      be seen by the progress view, rather than being poked in here.
4906.1.2 by John Arbash Meinel
Get the basic interface tested.
579
        self._total_byte_count += byte_count
580
        self._bytes_since_update += byte_count
4906.1.5 by John Arbash Meinel
Include the KiB/s for the transfer.
581
        if self._first_byte_time is None:
582
            # Note that this isn't great, as technically it should be the time
583
            # when the bytes started transferring, not when they completed.
584
            # However, we usually start with a small request anyway.
585
            self._first_byte_time = time.time()
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
586
        if direction in self._bytes_by_direction:
587
            self._bytes_by_direction[direction] += byte_count
588
        else:
589
            self._bytes_by_direction['unknown'] += byte_count
4912.1.4 by Martin Pool
Rename to -Dno_activity; incidentally fixes ReST syntax error
590
        if 'no_activity' in debug.debug_flags:
4912.1.1 by Martin Pool
Transport activity indicator is now shown even if there's no pb
591
            # Can be used as a workaround if
592
            # <https://launchpad.net/bugs/321935> reappears and transport
593
            # activity is cluttering other output.  However, thanks to
594
            # TextUIOutputStream this shouldn't be a problem any more.
4480.1.1 by Martin Pool
(mbp) only show transport activity when progress is already visible
595
            return
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
596
        now = time.time()
4912.1.3 by Martin Pool
Revert: don't show transport activity til some data has been sent.
597
        if self._total_byte_count < 2000:
598
            # a little resistance at first, so it doesn't stay stuck at 0
599
            # while connecting...
600
            return
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
601
        if self._transport_update_time is None:
602
            self._transport_update_time = now
4043.1.1 by John Arbash Meinel
Increase the debounce time for 'transport activity' to 0.5s
603
        elif now >= (self._transport_update_time + 0.5):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
604
            # guard against clock stepping backwards, and don't update too
605
            # often
4989.1.6 by Vincent Ladeuil
Add comments and update HACKING.txt about which units should be used.
606
            rate = (self._bytes_since_update
607
                    / (now - self._transport_update_time))
608
            # using base-10 units (see HACKING.txt).
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
609
            msg = ("%6dkB %5dkB/s " %
4989.1.1 by Gordon Tyler
Changed show_transport_activity and log_transport_activity to use base-10 SI units.
610
                    (self._total_byte_count / 1000, int(rate) / 1000,))
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
611
            self._transport_update_time = now
612
            self._last_repaint = now
613
            self._bytes_since_update = 0
614
            self._last_transport_msg = msg
615
            self._repaint()
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
616
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
617
    def _format_bytes_by_direction(self):
4906.1.5 by John Arbash Meinel
Include the KiB/s for the transfer.
618
        if self._first_byte_time is None:
619
            bps = 0.0
620
        else:
621
            transfer_time = time.time() - self._first_byte_time
622
            if transfer_time < 0.001:
623
                transfer_time = 0.001
624
            bps = self._total_byte_count / transfer_time
625
4989.1.6 by Vincent Ladeuil
Add comments and update HACKING.txt about which units should be used.
626
        # using base-10 units (see HACKING.txt).
4989.1.1 by Gordon Tyler
Changed show_transport_activity and log_transport_activity to use base-10 SI units.
627
        msg = ('Transferred: %.0fkB'
628
               ' (%.1fkB/s r:%.0fkB w:%.0fkB'
629
               % (self._total_byte_count / 1000.,
630
                  bps / 1000.,
631
                  self._bytes_by_direction['read'] / 1000.,
632
                  self._bytes_by_direction['write'] / 1000.,
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
633
                 ))
634
        if self._bytes_by_direction['unknown'] > 0:
4989.1.1 by Gordon Tyler
Changed show_transport_activity and log_transport_activity to use base-10 SI units.
635
            msg += ' u:%.0fkB)' % (
636
                self._bytes_by_direction['unknown'] / 1000.
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
637
                )
638
        else:
639
            msg += ')'
640
        return msg
641
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
642
    def log_transport_activity(self, display=False):
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
643
        msg = self._format_bytes_by_direction()
644
        trace.mutter(msg)
4906.1.7 by John Arbash Meinel
Switch to KiB/K for each value. Don't display if there are no bytes.
645
        if display and self._total_byte_count > 0:
4906.1.3 by John Arbash Meinel
Use clear() so that we clear a progress bar, but don't introduce a newline.
646
            self.clear()
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
647
            self._term_file.write(msg + '\n')
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
648
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
649
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
650
def _get_stream_encoding(stream):
651
    encoding = config.GlobalStack().get('output_encoding')
652
    if encoding is None:
653
        encoding = getattr(stream, "encoding", None)
654
    if encoding is None:
655
        encoding = osutils.get_terminal_encoding(trace=True)
656
    return encoding
657
658
659
def _unwrap_stream(stream):
660
    inner = getattr(stream, "buffer", None)
661
    if inner is None:
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
662
        inner = getattr(stream, "stream", stream)
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
663
    return inner
664
665
666
def _wrap_in_stream(stream, encoding=None, errors='replace'):
667
    if encoding is None:
668
        encoding = _get_stream_encoding(stream)
669
    encoded_stream = codecs.getreader(encoding)(stream, errors=errors)
670
    encoded_stream.encoding = encoding
671
    return encoded_stream
672
673
674
def _wrap_out_stream(stream, encoding=None, errors='replace'):
675
    if encoding is None:
676
        encoding = _get_stream_encoding(stream)
677
    encoded_stream = codecs.getwriter(encoding)(stream, errors=errors)
678
    encoded_stream.encoding = encoding
679
    return encoded_stream
680
681
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
682
class TextUIOutputStream(object):
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
683
    """Decorates stream to interact better with progress and change encoding.
684
685
    Before writing to the wrapped stream, progress is cleared. Callers must
686
    ensure bulk output is terminated with a newline so progress won't overwrite
687
    partial lines.
688
689
    Additionally, the encoding and errors behaviour of the underlying stream
690
    can be changed at this point. If errors is set to 'exact' raw bytes may be
691
    written to the underlying stream.
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
692
    """
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
693
694
    def __init__(self, ui_factory, stream, encoding=None, errors='strict'):
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
695
        self.ui_factory = ui_factory
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
696
        # GZ 2017-05-21: Clean up semantics when callers are made saner.
697
        inner = _unwrap_stream(stream)
698
        self.raw_stream = None
699
        if errors == "exact":
700
            errors = "strict"
701
            self.raw_stream = inner
702
        if inner is None:
703
            self.wrapped_stream = stream
704
            if encoding is None:
705
                encoding = _get_stream_encoding(stream)
706
        else:
707
            self.wrapped_stream = _wrap_out_stream(inner, encoding, errors)
708
            if encoding is None:
709
                encoding = self.wrapped_stream.encoding
710
        self.encoding = encoding
711
        self.errors = errors
712
713
    def _write(self, to_write):
714
        if isinstance(to_write, bytes):
715
            try:
716
                to_write = to_write.decode(self.encoding, self.errors)
717
            except UnicodeDecodeError:
718
                self.raw_stream.write(to_write)
719
                return
720
        self.wrapped_stream.write(to_write)
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
721
4792.8.7 by Martin Pool
Add TextUIOutputStream.flush
722
    def flush(self):
723
        self.ui_factory.clear_term()
724
        self.wrapped_stream.flush()
725
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
726
    def write(self, to_write):
727
        self.ui_factory.clear_term()
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
728
        self._write(to_write)
4792.8.3 by Martin Pool
Add TextUIOutputStream.writelines
729
730
    def writelines(self, lines):
731
        self.ui_factory.clear_term()
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
732
        for line in lines:
733
            self._write(line)