/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
7143.15.6 by Jelmer Vernooij
Merge trunk.
70
                self.ui.raw_stdin == _unwrap_stream(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()
7143.15.2 by Jelmer Vernooij
Run autopep8.
126
        if char == chr(3):  # INTR
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
127
            raise KeyboardInterrupt
7143.15.2 by Jelmer Vernooij
Run autopep8.
128
        if char == chr(4):  # EOF (^d, C-d)
6182.2.28 by Benoît Pierre
Cleanup helper class for TextUIFactory.choose.
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')
7143.15.2 by Jelmer Vernooij
Run autopep8.
305
        if pb_type == 'none':  # Explicit requirement
6561.2.1 by Vincent Ladeuil
Add a ``progress_bar`` config option.
306
            return NullProgressView()
7143.15.11 by Jelmer Vernooij
Fix syntax.
307
        if (pb_type == 'text' or # Explicit requirement
7143.15.2 by Jelmer Vernooij
Run autopep8.
308
                progress._supports_progress(self.stderr)):  # Guess
6561.2.1 by Vincent Ladeuil
Add a ``progress_bar`` config option.
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)
7143.10.1 by Jelmer Vernooij
Fix interactivity on Python 3.
335
        self.stderr.flush()
4449.3.18 by Martin Pool
Fuse CLIUIFactory and TextUIFactory and deprecate the old name
336
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
337
    def report_transport_activity(self, transport, byte_count, direction):
338
        """Called by transports as they do IO.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
339
3882.7.5 by Martin Pool
Further mockup of transport-based activity indicator.
340
        This may update a progress bar, spinner, or similar display.
341
        By default it does nothing.
342
        """
4449.2.1 by Martin Pool
TextUIFactory now respects BZR_PROGRESS_BAR again
343
        self._progress_view.show_transport_activity(transport,
7143.15.2 by Jelmer Vernooij
Run autopep8.
344
                                                    direction, byte_count)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
345
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
346
    def log_transport_activity(self, display=False):
347
        """See UIFactory.log_transport_activity()"""
348
        log = getattr(self._progress_view, 'log_transport_activity', None)
349
        if log is not None:
350
            log(display=display)
351
4711.1.7 by Martin Pool
Add UIFactory.show_error, show_warning, show_message
352
    def show_error(self, msg):
353
        self.clear_term()
354
        self.stderr.write("bzr: error: %s\n" % msg)
355
4711.1.8 by Martin Pool
Add show_warning and show_message tests and implementations
356
    def show_message(self, msg):
357
        self.note(msg)
358
359
    def show_warning(self, msg):
360
        self.clear_term()
361
        self.stderr.write("bzr: warning: %s\n" % msg)
362
3948.2.3 by Martin Pool
Make the interface from ProgressTask to ui more private
363
    def _progress_updated(self, task):
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
364
        """A task has been updated and wants to be displayed.
365
        """
4070.1.1 by Martin Pool
Be more robust about pb updates when none are active
366
        if not self._task_stack:
367
            warnings.warn("%r updated but no tasks are active" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
368
                          (task,))
4070.1.1 by Martin Pool
Be more robust about pb updates when none are active
369
        elif task != self._task_stack[-1]:
4961.2.19 by Martin Pool
Suppress un-helpful warning about progress task ordering
370
            # We used to check it was the top task, but it's hard to always
371
            # get this right and it's not necessarily useful: any actual
372
            # problems will be evident in use
7143.15.2 by Jelmer Vernooij
Run autopep8.
373
            # warnings.warn("%r is not the top progress task %r" %
4961.2.19 by Martin Pool
Suppress un-helpful warning about progress task ordering
374
            #     (task, self._task_stack[-1]))
375
            pass
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
376
        self._progress_view.show_progress(task)
377
3948.2.5 by Martin Pool
rename to _progress_all_finished
378
    def _progress_all_finished(self):
3948.2.3 by Martin Pool
Make the interface from ProgressTask to ui more private
379
        self._progress_view.clear()
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
380
4634.144.8 by Martin Pool
Generalize to ui_factory.show_user_warning
381
    def show_user_warning(self, warning_id, **message_args):
4634.144.5 by Martin Pool
Cleaner presentation and tests for warn_cross_format_fetch
382
        """Show a text message to the user.
383
384
        Explicitly not for warnings about bzr apis, deprecations or internals.
385
        """
386
        # eventually trace.warning should migrate here, to avoid logging and
387
        # be easier to test; that has a lot of test fallout so for now just
388
        # new code can call this
4634.144.11 by Martin Pool
Rename squelched to suppressed
389
        if warning_id not in self.suppressed_warnings:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
390
            warning = self.format_user_warning(warning_id, message_args)
391
            self.stderr.write(warning + '\n')
392
393
394
def pad_to_width(line, width, encoding_hint='ascii'):
395
    """Truncate or pad unicode line to width.
396
397
    This is best-effort for now, and strings containing control codes or
398
    non-ascii text may be cut and padded incorrectly.
399
    """
400
    s = line.encode(encoding_hint, 'replace')
6621.2.26 by Martin
Misc set of changes to get started with selftest on Python 3
401
    return (b'%-*.*s' % (width, width, s)).decode(encoding_hint)
4634.144.5 by Martin Pool
Cleaner presentation and tests for warn_cross_format_fetch
402
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
403
404
class TextProgressView(object):
405
    """Display of progress bar and other information on a tty.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
406
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
407
    This shows one line of text, including possibly a network indicator,
408
    spinner, progress bar, message, etc.
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
409
410
    One instance of this is created and held by the UI, and fed updates when a
411
    task wants to be painted.
412
413
    Transports feed data to this through the ui_factory object.
3948.2.2 by Martin Pool
Corrections to finishing progress bars
414
415
    The Progress views can comprise a tree with _parent_task pointers, but
416
    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
417
    """
418
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
419
    def __init__(self, term_file, encoding=None, errors=None):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
420
        self._term_file = term_file
6437.57.3 by Martin Packman
Make TextProgressView somewhat aware of encodings
421
        if encoding is None:
6437.57.7 by Martin Packman
Correct and test fallback to ascii logic when a stream has no encoding
422
            self._encoding = getattr(term_file, "encoding", None) or "ascii"
6437.57.3 by Martin Packman
Make TextProgressView somewhat aware of encodings
423
        else:
424
            self._encoding = encoding
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
425
        # true when there's output on the screen we may need to clear
426
        self._have_output = False
427
        self._last_transport_msg = ''
428
        self._spin_pos = 0
429
        # time we last repainted the screen
430
        self._last_repaint = 0
431
        # time we last got information about transport activity
432
        self._transport_update_time = 0
433
        self._last_task = None
434
        self._total_byte_count = 0
435
        self._bytes_since_update = 0
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
436
        self._bytes_by_direction = {'unknown': 0, 'read': 0, 'write': 0}
4906.1.5 by John Arbash Meinel
Include the KiB/s for the transfer.
437
        self._first_byte_time = None
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
438
        self._fraction = 0
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
439
        # 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.
440
        # correspond reliably to overall command progress
441
        self.enable_bar = False
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
442
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
443
    def _avail_width(self):
444
        # 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
445
        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
446
        if w is None:
5050.16.1 by Martin Pool
Clear off progress bars by painting spaces.
447
            return None
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
448
        else:
449
            return w - 1
450
6437.57.3 by Martin Packman
Make TextProgressView somewhat aware of encodings
451
    def _show_line(self, u):
5050.16.1 by Martin Pool
Clear off progress bars by painting spaces.
452
        width = self._avail_width()
453
        if width is not None:
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
454
            u = pad_to_width(u, width, encoding_hint=self._encoding)
455
        self._term_file.write('\r' + u + '\r')
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
456
457
    def clear(self):
458
        if self._have_output:
459
            self._show_line('')
460
        self._have_output = False
461
462
    def _render_bar(self):
463
        # 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.
464
        if self.enable_bar and (
7143.15.2 by Jelmer Vernooij
Run autopep8.
465
                (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
466
            # If there's no task object, we show space for the bar anyhow.
467
            # That's because most invocations of bzr will end showing progress
468
            # at some point, though perhaps only after doing some initial IO.
469
            # It looks better to draw the progress bar initially rather than
470
            # to have what looks like an incomplete progress bar.
7143.15.2 by Jelmer Vernooij
Run autopep8.
471
            spin_str = r'/-\|'[self._spin_pos % 4]
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
472
            self._spin_pos += 1
473
            cols = 20
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
474
            if self._last_task is None:
475
                completion_fraction = 0
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
476
                self._fraction = 0
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
477
            else:
478
                completion_fraction = \
479
                    self._last_task._overall_completion_fraction() or 0
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
480
            if (completion_fraction < self._fraction and 'progress' in
7143.15.2 by Jelmer Vernooij
Run autopep8.
481
                    debug.debug_flags):
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
482
                debug.set_trace()
4332.3.18 by Robert Collins
Add -Dprogress to assist in debugging progress bar jumping.
483
            self._fraction = completion_fraction
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
484
            markers = int(round(float(cols) * completion_fraction)) - 1
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
485
            bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
486
            return bar_str
4912.1.1 by Martin Pool
Transport activity indicator is now shown even if there's no pb
487
        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
488
            # The last task wanted just a spinner, no bar
7143.15.2 by Jelmer Vernooij
Run autopep8.
489
            spin_str = r'/-\|'[self._spin_pos % 4]
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
490
            self._spin_pos += 1
491
            return spin_str + ' '
492
        else:
493
            return ''
494
495
    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
496
        """Format task-specific parts of progress bar.
497
498
        :returns: (text_part, counter_part) both unicode strings.
499
        """
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
500
        if not task.show_count:
501
            s = ''
4017.1.1 by John Arbash Meinel
Get a pb.tick() to work after calling pb.update()
502
        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
503
            s = ' %d/%d' % (task.current_cnt, task.total_cnt)
504
        elif task.current_cnt is not None:
505
            s = ' %d' % (task.current_cnt)
506
        else:
507
            s = ''
508
        # compose all the parent messages
509
        t = task
510
        m = task.msg
511
        while t._parent_task:
512
            t = t._parent_task
513
            if t.msg:
514
                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
515
        return m, s
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
516
4110.2.16 by Martin Pool
Refactor TextProgressView a bit and add another test
517
    def _render_line(self):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
518
        bar_string = self._render_bar()
519
        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
520
            task_part, counter_part = self._format_task(self._last_task)
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
521
        else:
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
522
            task_part = counter_part = ''
4580.3.5 by Martin Pool
selftest sets ProgressTask.show_transport_activity off
523
        if self._last_task and not self._last_task.show_transport_activity:
524
            trans = ''
525
        else:
526
            trans = self._last_transport_msg
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
527
        # the bar separates the transport activity from the message, so even
528
        # if there's no bar or spinner, we must show something if both those
529
        # fields are present
530
        if (task_part or trans) and not bar_string:
531
            bar_string = '| '
5339.2.1 by Martin Pool
Progress bars should truncate text rather than counters so as not to give a misleading result
532
        # preferentially truncate the task message if we don't have enough
533
        # space
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
534
        avail_width = self._avail_width()
535
        if avail_width is not None:
536
            # if terminal avail_width is unknown, don't truncate
7143.15.2 by Jelmer Vernooij
Run autopep8.
537
            current_len = len(bar_string) + len(trans) + \
538
                len(task_part) + len(counter_part)
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
539
            # 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.
540
            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
541
            if gap > 0:
7143.15.2 by Jelmer Vernooij
Run autopep8.
542
                task_part = task_part[:-gap - 2] + '..'
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
543
        s = trans + bar_string + task_part + counter_part
544
        if avail_width is not None:
545
            if len(s) < avail_width:
546
                s = s.ljust(avail_width)
547
            elif len(s) > avail_width:
548
                s = s[:avail_width]
549
        return s
4110.2.16 by Martin Pool
Refactor TextProgressView a bit and add another test
550
551
    def _repaint(self):
552
        s = self._render_line()
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
553
        self._show_line(s)
554
        self._have_output = True
555
556
    def show_progress(self, task):
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
557
        """Called by the task object when it has changed.
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
558
559
        :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
560
            by following links.
561
        """
4110.2.18 by Martin Pool
Progress bars always repaint when task structure is changed
562
        must_update = task is not self._last_task
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
563
        self._last_task = task
564
        now = time.time()
4580.3.1 by Martin Pool
ProgressTasks can specify an update latency
565
        if (not must_update) and (now < self._last_repaint + task.update_latency):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
566
            return
4110.2.15 by Martin Pool
Fix bug in showing task progress and add a test
567
        if now > self._transport_update_time + 10:
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
568
            # no recent activity; expire it
569
            self._last_transport_msg = ''
570
        self._last_repaint = now
571
        self._repaint()
572
4449.2.1 by Martin Pool
TextUIFactory now respects BZR_PROGRESS_BAR again
573
    def show_transport_activity(self, transport, direction, byte_count):
4110.2.19 by Martin Pool
Transport activity now shows scheme and direction
574
        """Called by transports via the ui_factory, as they do IO.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
575
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
576
        This may update a progress bar, spinner, or similar display.
577
        By default it does nothing.
578
        """
4906.1.8 by John Arbash Meinel
Merge bzr.dev, resolve conflicts.
579
        # XXX: there should be a transport activity model, and that too should
580
        #      be seen by the progress view, rather than being poked in here.
4906.1.2 by John Arbash Meinel
Get the basic interface tested.
581
        self._total_byte_count += byte_count
582
        self._bytes_since_update += byte_count
4906.1.5 by John Arbash Meinel
Include the KiB/s for the transfer.
583
        if self._first_byte_time is None:
584
            # Note that this isn't great, as technically it should be the time
585
            # when the bytes started transferring, not when they completed.
586
            # However, we usually start with a small request anyway.
587
            self._first_byte_time = time.time()
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
588
        if direction in self._bytes_by_direction:
589
            self._bytes_by_direction[direction] += byte_count
590
        else:
591
            self._bytes_by_direction['unknown'] += byte_count
4912.1.4 by Martin Pool
Rename to -Dno_activity; incidentally fixes ReST syntax error
592
        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
593
            # Can be used as a workaround if
594
            # <https://launchpad.net/bugs/321935> reappears and transport
595
            # activity is cluttering other output.  However, thanks to
596
            # 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
597
            return
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
598
        now = time.time()
4912.1.3 by Martin Pool
Revert: don't show transport activity til some data has been sent.
599
        if self._total_byte_count < 2000:
600
            # a little resistance at first, so it doesn't stay stuck at 0
601
            # while connecting...
602
            return
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
603
        if self._transport_update_time is None:
604
            self._transport_update_time = now
4043.1.1 by John Arbash Meinel
Increase the debounce time for 'transport activity' to 0.5s
605
        elif now >= (self._transport_update_time + 0.5):
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
606
            # guard against clock stepping backwards, and don't update too
607
            # often
7143.15.2 by Jelmer Vernooij
Run autopep8.
608
            rate = (self._bytes_since_update /
609
                    (now - self._transport_update_time))
4989.1.6 by Vincent Ladeuil
Add comments and update HACKING.txt about which units should be used.
610
            # using base-10 units (see HACKING.txt).
5339.2.3 by Martin Pool
Show the progress spinner between the transport rate and the message.
611
            msg = ("%6dkB %5dkB/s " %
7143.15.2 by Jelmer Vernooij
Run autopep8.
612
                   (self._total_byte_count / 1000, int(rate) / 1000,))
3882.8.9 by Martin Pool
Move TextProgressView to ui.text
613
            self._transport_update_time = now
614
            self._last_repaint = now
615
            self._bytes_since_update = 0
616
            self._last_transport_msg = msg
617
            self._repaint()
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
618
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
619
    def _format_bytes_by_direction(self):
4906.1.5 by John Arbash Meinel
Include the KiB/s for the transfer.
620
        if self._first_byte_time is None:
621
            bps = 0.0
622
        else:
623
            transfer_time = time.time() - self._first_byte_time
624
            if transfer_time < 0.001:
625
                transfer_time = 0.001
626
            bps = self._total_byte_count / transfer_time
627
4989.1.6 by Vincent Ladeuil
Add comments and update HACKING.txt about which units should be used.
628
        # 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.
629
        msg = ('Transferred: %.0fkB'
630
               ' (%.1fkB/s r:%.0fkB w:%.0fkB'
631
               % (self._total_byte_count / 1000.,
632
                  bps / 1000.,
633
                  self._bytes_by_direction['read'] / 1000.,
634
                  self._bytes_by_direction['write'] / 1000.,
7143.15.2 by Jelmer Vernooij
Run autopep8.
635
                  ))
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
636
        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.
637
            msg += ' u:%.0fkB)' % (
638
                self._bytes_by_direction['unknown'] / 1000.
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
639
                )
640
        else:
641
            msg += ')'
642
        return msg
643
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
644
    def log_transport_activity(self, display=False):
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
645
        msg = self._format_bytes_by_direction()
646
        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.
647
        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.
648
            self.clear()
4906.1.4 by John Arbash Meinel
Play around with the ui display a bit more.
649
            self._term_file.write(msg + '\n')
4906.1.1 by John Arbash Meinel
Basic implementation of logging bytes transferred when bzr exits.
650
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
651
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
652
def _get_stream_encoding(stream):
653
    encoding = config.GlobalStack().get('output_encoding')
654
    if encoding is None:
655
        encoding = getattr(stream, "encoding", None)
656
    if encoding is None:
657
        encoding = osutils.get_terminal_encoding(trace=True)
658
    return encoding
659
660
661
def _unwrap_stream(stream):
662
    inner = getattr(stream, "buffer", None)
663
    if inner is None:
6910.3.1 by Martin
Delay stream wrapping and fix choose in char based mode
664
        inner = getattr(stream, "stream", stream)
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
665
    return inner
666
667
668
def _wrap_in_stream(stream, encoding=None, errors='replace'):
669
    if encoding is None:
670
        encoding = _get_stream_encoding(stream)
671
    encoded_stream = codecs.getreader(encoding)(stream, errors=errors)
672
    encoded_stream.encoding = encoding
673
    return encoded_stream
674
675
676
def _wrap_out_stream(stream, encoding=None, errors='replace'):
677
    if encoding is None:
678
        encoding = _get_stream_encoding(stream)
679
    encoded_stream = codecs.getwriter(encoding)(stream, errors=errors)
680
    encoded_stream.encoding = encoding
681
    return encoded_stream
682
683
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
684
class TextUIOutputStream(object):
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
685
    """Decorates stream to interact better with progress and change encoding.
686
687
    Before writing to the wrapped stream, progress is cleared. Callers must
688
    ensure bulk output is terminated with a newline so progress won't overwrite
689
    partial lines.
690
691
    Additionally, the encoding and errors behaviour of the underlying stream
692
    can be changed at this point. If errors is set to 'exact' raw bytes may be
693
    written to the underlying stream.
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
694
    """
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
695
696
    def __init__(self, ui_factory, stream, encoding=None, errors='strict'):
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
697
        self.ui_factory = ui_factory
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
698
        # GZ 2017-05-21: Clean up semantics when callers are made saner.
699
        inner = _unwrap_stream(stream)
700
        self.raw_stream = None
701
        if errors == "exact":
702
            errors = "strict"
703
            self.raw_stream = inner
704
        if inner is None:
705
            self.wrapped_stream = stream
706
            if encoding is None:
707
                encoding = _get_stream_encoding(stream)
708
        else:
709
            self.wrapped_stream = _wrap_out_stream(inner, encoding, errors)
710
            if encoding is None:
711
                encoding = self.wrapped_stream.encoding
712
        self.encoding = encoding
713
        self.errors = errors
714
715
    def _write(self, to_write):
716
        if isinstance(to_write, bytes):
717
            try:
718
                to_write = to_write.decode(self.encoding, self.errors)
719
            except UnicodeDecodeError:
720
                self.raw_stream.write(to_write)
721
                return
722
        self.wrapped_stream.write(to_write)
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
723
4792.8.7 by Martin Pool
Add TextUIOutputStream.flush
724
    def flush(self):
725
        self.ui_factory.clear_term()
726
        self.wrapped_stream.flush()
727
4792.8.1 by Martin Pool
Add TextUIOutputStream coordinated with progress view
728
    def write(self, to_write):
729
        self.ui_factory.clear_term()
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
730
        self._write(to_write)
4792.8.3 by Martin Pool
Add TextUIOutputStream.writelines
731
732
    def writelines(self, lines):
733
        self.ui_factory.clear_term()
6621.22.1 by Martin
Refactor bzrlib.ui to be based on unicode streams
734
        for line in lines:
735
            self._write(line)