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