/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/ui/text.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Text UI, write output to the console."""
18
17
 
19
 
from __future__ import absolute_import
 
18
"""Text UI, write output to the console.
 
19
"""
20
20
 
21
21
import codecs
 
22
import getpass
22
23
import os
23
24
import sys
 
25
import time
24
26
import warnings
25
27
 
26
 
from ..lazy_import import lazy_import
 
28
from bzrlib.lazy_import import lazy_import
27
29
lazy_import(globals(), """
28
 
import getpass
29
 
import time
30
 
 
31
 
from breezy import (
 
30
from bzrlib import (
32
31
    debug,
33
32
    progress,
34
 
    )
35
 
""")
36
 
 
37
 
from .. import (
38
 
    config,
39
33
    osutils,
 
34
    symbol_versioning,
40
35
    trace,
41
36
    )
42
 
from ..sixish import (
43
 
    text_type,
44
 
    )
45
 
from . import (
 
37
 
 
38
""")
 
39
 
 
40
from bzrlib.osutils import watch_sigwinch
 
41
 
 
42
from bzrlib.ui import (
 
43
    UIFactory,
46
44
    NullProgressView,
47
 
    UIFactory,
48
45
    )
49
46
 
50
47
 
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
 
 
64
 
        Line-based input is used if the BRZ_TEXTUI_INPUT environment
65
 
        variable is set to 'line-based', or if there is no controlling
66
 
        terminal.
67
 
        """
68
 
        is_tty = self.ui.raw_stdin.isatty()
69
 
        if (os.environ.get('BRZ_TEXTUI_INPUT') != 'line-based' and
70
 
                self.ui.raw_stdin == sys.stdin and is_tty):
71
 
            self.line_based = False
72
 
            self.echo_back = True
73
 
        else:
74
 
            self.line_based = True
75
 
            self.echo_back = not is_tty
76
 
 
77
 
    def _build_alternatives(self, msg, choices, default):
78
 
        """Parse choices string.
79
 
 
80
 
        Setup final prompt and the lists of choices and associated
81
 
        shortcuts.
82
 
        """
83
 
        index = 0
84
 
        help_list = []
85
 
        self.alternatives = {}
86
 
        choices = choices.split('\n')
87
 
        if default is not None and default not in range(0, len(choices)):
88
 
            raise ValueError("invalid default index")
89
 
        for c in choices:
90
 
            name = c.replace('&', '').lower()
91
 
            choice = (name, index)
92
 
            if name in self.alternatives:
93
 
                raise ValueError("duplicated choice: %s" % name)
94
 
            self.alternatives[name] = choice
95
 
            shortcut = c.find('&')
96
 
            if -1 != shortcut and (shortcut + 1) < len(c):
97
 
                help = c[:shortcut]
98
 
                help += '[' + c[shortcut + 1] + ']'
99
 
                help += c[(shortcut + 2):]
100
 
                shortcut = c[shortcut + 1]
101
 
            else:
102
 
                c = c.replace('&', '')
103
 
                shortcut = c[0]
104
 
                help = '[%s]%s' % (shortcut, c[1:])
105
 
            shortcut = shortcut.lower()
106
 
            if shortcut in self.alternatives:
107
 
                raise ValueError("duplicated shortcut: %s" % shortcut)
108
 
            self.alternatives[shortcut] = choice
109
 
            # Add redirections for default.
110
 
            if index == default:
111
 
                self.alternatives[''] = choice
112
 
                self.alternatives['\r'] = choice
113
 
            help_list.append(help)
114
 
            index += 1
115
 
 
116
 
        self.prompt = u'%s (%s): ' % (msg, ', '.join(help_list))
117
 
 
118
 
    def _getline(self):
119
 
        line = self.ui.stdin.readline()
120
 
        if '' == line:
121
 
            raise EOFError
122
 
        return line.strip()
123
 
 
124
 
    def _getchar(self):
125
 
        char = osutils.getchar()
126
 
        if char == chr(3): # INTR
127
 
            raise KeyboardInterrupt
128
 
        if char == chr(4): # EOF (^d, C-d)
129
 
            raise EOFError
130
 
        if isinstance(char, bytes):
131
 
            return char.decode('ascii', 'replace')
132
 
        return char
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:
149
 
                self.ui.stderr.write(u'\n')
150
 
                return None
151
 
            except KeyboardInterrupt:
152
 
                self.ui.stderr.write(u'\n')
153
 
                raise
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:
160
 
                self.ui.stderr.write(name + u'\n')
161
 
            return index
162
 
 
163
 
 
164
 
opt_progress_bar = config.Option(
165
 
    'progress_bar', help='Progress bar type.',
166
 
    default_from_env=['BRZ_PROGRESS_BAR'], default=None,
167
 
    invalid='error')
168
 
 
169
 
 
170
48
class TextUIFactory(UIFactory):
171
 
    """A UI factory for Text user interfaces."""
 
49
    """A UI factory for Text user interefaces."""
172
50
 
173
 
    def __init__(self, stdin, stdout, stderr):
174
 
        """Create a TextUIFactory."""
 
51
    def __init__(self,
 
52
                 stdin=None,
 
53
                 stdout=None,
 
54
                 stderr=None):
 
55
        """Create a TextUIFactory.
 
56
        """
175
57
        super(TextUIFactory, self).__init__()
 
58
        # TODO: there's no good reason not to pass all three streams, maybe we
 
59
        # should deprecate the default values...
176
60
        self.stdin = stdin
177
61
        self.stdout = stdout
178
62
        self.stderr = stderr
179
 
        self._progress_view = NullProgressView()
180
 
 
181
 
    def __enter__(self):
182
 
        # Choose default encoding and handle py2/3 differences
183
 
        self._setup_streams()
184
63
        # paints progress, network activity, etc
185
64
        self._progress_view = self.make_progress_view()
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)
195
 
 
196
 
    def choose(self, msg, choices, default=None):
197
 
        """Prompt the user for a list of alternatives.
198
 
 
199
 
        Support both line-based and char-based editing.
200
 
 
201
 
        In line-based mode, both the shortcut and full choice name are valid
202
 
        answers, e.g. for choose('prompt', '&yes\n&no'): 'y', ' Y ', ' yes',
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
 
        """
213
 
 
214
 
        choose_ui = _ChooseUI(self, msg, choices, default)
215
 
        return choose_ui.interact()
 
65
        # hook up the signals to watch for terminal size changes
 
66
        watch_sigwinch()
216
67
 
217
68
    def be_quiet(self, state):
218
69
        if state and not self._quiet:
231
82
        # to clear it.  We might need to separately check for the case of
232
83
        self._progress_view.clear()
233
84
 
 
85
    def get_boolean(self, prompt):
 
86
        while True:
 
87
            self.prompt(prompt + "? [y/n]: ")
 
88
            line = self.stdin.readline().lower()
 
89
            if line in ('y\n', 'yes\n'):
 
90
                return True
 
91
            elif line in ('n\n', 'no\n'):
 
92
                return False
 
93
            elif line in ('', None):
 
94
                # end-of-file; possibly should raise an error here instead
 
95
                return None
 
96
 
234
97
    def get_integer(self, prompt):
235
98
        while True:
236
99
            self.prompt(prompt)
251
114
            password = self.stdin.readline()
252
115
            if not password:
253
116
                password = None
254
 
            else:
255
 
                if password[-1] == '\n':
256
 
                    password = password[:-1]
 
117
            elif password[-1] == '\n':
 
118
                password = password[:-1]
257
119
        return password
258
120
 
259
 
    def get_password(self, prompt=u'', **kwargs):
 
121
    def get_password(self, prompt='', **kwargs):
260
122
        """Prompt the user for a password.
261
123
 
262
124
        :param prompt: The prompt to present the user
287
149
        username = self.stdin.readline()
288
150
        if not username:
289
151
            username = None
290
 
        else:
291
 
            if username[-1] == '\n':
292
 
                username = username[:-1]
 
152
        elif username[-1] == '\n':
 
153
            username = username[:-1]
293
154
        return username
294
155
 
295
156
    def make_progress_view(self):
296
157
        """Construct and return a new ProgressView subclass for this UI.
297
158
        """
298
159
        # with --quiet, never any progress view
299
 
        # <https://bugs.launchpad.net/bzr/+bug/320035>.  Otherwise if the
 
160
        # <https://bugs.edge.launchpad.net/bzr/+bug/320035>.  Otherwise if the
300
161
        # user specifically requests either text or no progress bars, always
301
162
        # do that.  otherwise, guess based on $TERM and tty presence.
302
163
        if self.is_quiet():
303
164
            return NullProgressView()
304
 
        pb_type = config.GlobalStack().get('progress_bar')
305
 
        if pb_type == 'none': # Explicit requirement
306
 
            return NullProgressView()
307
 
        if (pb_type == 'text' # Explicit requirement
308
 
            or progress._supports_progress(self.stderr)): # Guess
309
 
            return TextProgressView(self.stderr)
310
 
        # No explicit requirement and no successful guess
311
 
        return NullProgressView()
 
165
        elif os.environ.get('BZR_PROGRESS_BAR') == 'text':
 
166
            return TextProgressView(self.stderr)
 
167
        elif os.environ.get('BZR_PROGRESS_BAR') == 'none':
 
168
            return NullProgressView()
 
169
        elif progress._supports_progress(self.stderr):
 
170
            return TextProgressView(self.stderr)
 
171
        else:
 
172
            return NullProgressView()
312
173
 
313
174
    def _make_output_stream_explicit(self, encoding, encoding_type):
314
 
        return TextUIOutputStream(self, self.stdout, encoding, encoding_type)
 
175
        if encoding_type == 'exact':
 
176
            # force sys.stdout to be binary stream on win32; 
 
177
            # NB: this leaves the file set in that mode; may cause problems if
 
178
            # one process tries to do binary and then text output
 
179
            if sys.platform == 'win32':
 
180
                fileno = getattr(self.stdout, 'fileno', None)
 
181
                if fileno:
 
182
                    import msvcrt
 
183
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
184
            return TextUIOutputStream(self, self.stdout)
 
185
        else:
 
186
            encoded_stdout = codecs.getwriter(encoding)(self.stdout,
 
187
                errors=encoding_type)
 
188
            # For whatever reason codecs.getwriter() does not advertise its encoding
 
189
            # it just returns the encoding of the wrapped file, which is completely
 
190
            # bogus. So set the attribute, so we can find the correct encoding later.
 
191
            encoded_stdout.encoding = encoding
 
192
            return TextUIOutputStream(self, encoded_stdout)
315
193
 
316
194
    def note(self, msg):
317
195
        """Write an already-formatted message, clearing the progress bar if necessary."""
320
198
 
321
199
    def prompt(self, prompt, **kwargs):
322
200
        """Emit prompt on the CLI.
323
 
 
 
201
        
324
202
        :param kwargs: Dictionary of arguments to insert into the prompt,
325
203
            to allow UIs to reformat the prompt.
326
204
        """
327
 
        if not isinstance(prompt, text_type):
328
 
            raise ValueError("prompt %r not a unicode string" % prompt)
329
205
        if kwargs:
330
206
            # See <https://launchpad.net/bugs/365891>
331
207
            prompt = prompt % kwargs
 
208
        prompt = prompt.encode(osutils.get_terminal_encoding(), 'replace')
332
209
        self.clear_term()
333
 
        self.stdout.flush()
334
210
        self.stderr.write(prompt)
335
211
 
336
212
    def report_transport_activity(self, transport, byte_count, direction):
386
262
        # be easier to test; that has a lot of test fallout so for now just
387
263
        # new code can call this
388
264
        if warning_id not in self.suppressed_warnings:
389
 
            warning = self.format_user_warning(warning_id, message_args)
390
 
            self.stderr.write(warning + '\n')
391
 
 
392
 
 
393
 
def pad_to_width(line, width, encoding_hint='ascii'):
394
 
    """Truncate or pad unicode line to width.
395
 
 
396
 
    This is best-effort for now, and strings containing control codes or
397
 
    non-ascii text may be cut and padded incorrectly.
398
 
    """
399
 
    s = line.encode(encoding_hint, 'replace')
400
 
    return (b'%-*.*s' % (width, width, s)).decode(encoding_hint)
 
265
            self.stderr.write(self.format_user_warning(warning_id, message_args) +
 
266
                '\n')
401
267
 
402
268
 
403
269
class TextProgressView(object):
404
270
    """Display of progress bar and other information on a tty.
405
271
 
406
 
    This shows one line of text, including possibly a network indicator,
407
 
    spinner, progress bar, message, etc.
 
272
    This shows one line of text, including possibly a network indicator, spinner,
 
273
    progress bar, message, etc.
408
274
 
409
275
    One instance of this is created and held by the UI, and fed updates when a
410
276
    task wants to be painted.
415
281
    this only prints the stack from the nominated current task up to the root.
416
282
    """
417
283
 
418
 
    def __init__(self, term_file, encoding=None, errors=None):
 
284
    def __init__(self, term_file):
419
285
        self._term_file = term_file
420
 
        if encoding is None:
421
 
            self._encoding = getattr(term_file, "encoding", None) or "ascii"
422
 
        else:
423
 
            self._encoding = encoding
424
286
        # true when there's output on the screen we may need to clear
425
287
        self._have_output = False
426
288
        self._last_transport_msg = ''
435
297
        self._bytes_by_direction = {'unknown': 0, 'read': 0, 'write': 0}
436
298
        self._first_byte_time = None
437
299
        self._fraction = 0
438
 
        # force the progress bar to be off, as at the moment it doesn't
 
300
        # force the progress bar to be off, as at the moment it doesn't 
439
301
        # correspond reliably to overall command progress
440
302
        self.enable_bar = False
441
303
 
442
 
    def _avail_width(self):
443
 
        # we need one extra space for terminals that wrap on last char
444
 
        w = osutils.terminal_width()
445
 
        if w is None:
446
 
            return None
447
 
        else:
448
 
            return w - 1
449
 
 
450
 
    def _show_line(self, u):
451
 
        width = self._avail_width()
 
304
    def _show_line(self, s):
 
305
        # sys.stderr.write("progress %r\n" % s)
 
306
        width = osutils.terminal_width()
452
307
        if width is not None:
453
 
            u = pad_to_width(u, width, encoding_hint=self._encoding)
454
 
        self._term_file.write('\r' + u + '\r')
 
308
            # we need one extra space for terminals that wrap on last char
 
309
            width = width - 1
 
310
            s = '%-*.*s' % (width, width, s)
 
311
        self._term_file.write('\r' + s + '\r')
455
312
 
456
313
    def clear(self):
457
314
        if self._have_output:
478
335
                    self._last_task._overall_completion_fraction() or 0
479
336
            if (completion_fraction < self._fraction and 'progress' in
480
337
                debug.debug_flags):
481
 
                debug.set_trace()
 
338
                import pdb;pdb.set_trace()
482
339
            self._fraction = completion_fraction
483
340
            markers = int(round(float(cols) * completion_fraction)) - 1
484
341
            bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
492
349
            return ''
493
350
 
494
351
    def _format_task(self, task):
495
 
        """Format task-specific parts of progress bar.
496
 
 
497
 
        :returns: (text_part, counter_part) both unicode strings.
498
 
        """
499
352
        if not task.show_count:
500
353
            s = ''
501
354
        elif task.current_cnt is not None and task.total_cnt is not None:
511
364
            t = t._parent_task
512
365
            if t.msg:
513
366
                m = t.msg + ':' + m
514
 
        return m, s
 
367
        return m + s
515
368
 
516
369
    def _render_line(self):
517
370
        bar_string = self._render_bar()
518
371
        if self._last_task:
519
 
            task_part, counter_part = self._format_task(self._last_task)
 
372
            task_msg = self._format_task(self._last_task)
520
373
        else:
521
 
            task_part = counter_part = ''
 
374
            task_msg = ''
522
375
        if self._last_task and not self._last_task.show_transport_activity:
523
376
            trans = ''
524
377
        else:
525
378
            trans = self._last_transport_msg
526
 
        # the bar separates the transport activity from the message, so even
527
 
        # if there's no bar or spinner, we must show something if both those
528
 
        # fields are present
529
 
        if (task_part or trans) and not bar_string:
530
 
            bar_string = '| '
531
 
        # preferentially truncate the task message if we don't have enough
532
 
        # space
533
 
        avail_width = self._avail_width()
534
 
        if avail_width is not None:
535
 
            # if terminal avail_width is unknown, don't truncate
536
 
            current_len = len(bar_string) + len(trans) + len(task_part) + len(counter_part)
537
 
            # GZ 2017-04-22: Should measure and truncate task_part properly
538
 
            gap = current_len - avail_width
539
 
            if gap > 0:
540
 
                task_part = task_part[:-gap-2] + '..'
541
 
        s = trans + bar_string + task_part + counter_part
542
 
        if avail_width is not None:
543
 
            if len(s) < avail_width:
544
 
                s = s.ljust(avail_width)
545
 
            elif len(s) > avail_width:
546
 
                s = s[:avail_width]
547
 
        return s
 
379
            if trans:
 
380
                trans += ' | '
 
381
        return (bar_string + trans + task_msg)
548
382
 
549
383
    def _repaint(self):
550
384
        s = self._render_line()
553
387
 
554
388
    def show_progress(self, task):
555
389
        """Called by the task object when it has changed.
556
 
 
557
 
        :param task: The top task object; its parents are also included
 
390
        
 
391
        :param task: The top task object; its parents are also included 
558
392
            by following links.
559
393
        """
560
394
        must_update = task is not self._last_task
606
440
            rate = (self._bytes_since_update
607
441
                    / (now - self._transport_update_time))
608
442
            # using base-10 units (see HACKING.txt).
609
 
            msg = ("%6dkB %5dkB/s " %
 
443
            msg = ("%6dkB %5dkB/s" %
610
444
                    (self._total_byte_count / 1000, int(rate) / 1000,))
611
445
            self._transport_update_time = now
612
446
            self._last_repaint = now
647
481
            self._term_file.write(msg + '\n')
648
482
 
649
483
 
650
 
def _get_stream_encoding(stream):
651
 
    encoding = config.GlobalStack().get('output_encoding')
652
 
    if encoding is None:
653
 
        encoding = getattr(stream, "encoding", None)
654
 
    if encoding is None:
655
 
        encoding = osutils.get_terminal_encoding(trace=True)
656
 
    return encoding
657
 
 
658
 
 
659
 
def _unwrap_stream(stream):
660
 
    inner = getattr(stream, "buffer", None)
661
 
    if inner is None:
662
 
        inner = getattr(stream, "stream", stream)
663
 
    return inner
664
 
 
665
 
 
666
 
def _wrap_in_stream(stream, encoding=None, errors='replace'):
667
 
    if encoding is None:
668
 
        encoding = _get_stream_encoding(stream)
669
 
    encoded_stream = codecs.getreader(encoding)(stream, errors=errors)
670
 
    encoded_stream.encoding = encoding
671
 
    return encoded_stream
672
 
 
673
 
 
674
 
def _wrap_out_stream(stream, encoding=None, errors='replace'):
675
 
    if encoding is None:
676
 
        encoding = _get_stream_encoding(stream)
677
 
    encoded_stream = codecs.getwriter(encoding)(stream, errors=errors)
678
 
    encoded_stream.encoding = encoding
679
 
    return encoded_stream
680
 
 
681
 
 
682
484
class TextUIOutputStream(object):
683
 
    """Decorates stream to interact better with progress and change encoding.
684
 
 
685
 
    Before writing to the wrapped stream, progress is cleared. Callers must
686
 
    ensure bulk output is terminated with a newline so progress won't overwrite
687
 
    partial lines.
688
 
 
689
 
    Additionally, the encoding and errors behaviour of the underlying stream
690
 
    can be changed at this point. If errors is set to 'exact' raw bytes may be
691
 
    written to the underlying stream.
 
485
    """Decorates an output stream so that the terminal is cleared before writing.
 
486
 
 
487
    This is supposed to ensure that the progress bar does not conflict with bulk
 
488
    text output.
692
489
    """
693
 
 
694
 
    def __init__(self, ui_factory, stream, encoding=None, errors='strict'):
 
490
    # XXX: this does not handle the case of writing part of a line, then doing
 
491
    # progress bar output: the progress bar will probably write over it.
 
492
    # one option is just to buffer that text until we have a full line;
 
493
    # another is to save and restore it
 
494
 
 
495
    # XXX: might need to wrap more methods
 
496
 
 
497
    def __init__(self, ui_factory, wrapped_stream):
695
498
        self.ui_factory = ui_factory
696
 
        # GZ 2017-05-21: Clean up semantics when callers are made saner.
697
 
        inner = _unwrap_stream(stream)
698
 
        self.raw_stream = None
699
 
        if errors == "exact":
700
 
            errors = "strict"
701
 
            self.raw_stream = inner
702
 
        if inner is None:
703
 
            self.wrapped_stream = stream
704
 
            if encoding is None:
705
 
                encoding = _get_stream_encoding(stream)
706
 
        else:
707
 
            self.wrapped_stream = _wrap_out_stream(inner, encoding, errors)
708
 
            if encoding is None:
709
 
                encoding = self.wrapped_stream.encoding
710
 
        self.encoding = encoding
711
 
        self.errors = errors
712
 
 
713
 
    def _write(self, to_write):
714
 
        if isinstance(to_write, bytes):
715
 
            try:
716
 
                to_write = to_write.decode(self.encoding, self.errors)
717
 
            except UnicodeDecodeError:
718
 
                self.raw_stream.write(to_write)
719
 
                return
720
 
        self.wrapped_stream.write(to_write)
 
499
        self.wrapped_stream = wrapped_stream
 
500
        # this does no transcoding, but it must expose the underlying encoding
 
501
        # because some callers need to know what can be written - see for
 
502
        # example unescape_for_display.
 
503
        self.encoding = getattr(wrapped_stream, 'encoding', None)
721
504
 
722
505
    def flush(self):
723
506
        self.ui_factory.clear_term()
725
508
 
726
509
    def write(self, to_write):
727
510
        self.ui_factory.clear_term()
728
 
        self._write(to_write)
 
511
        self.wrapped_stream.write(to_write)
729
512
 
730
513
    def writelines(self, lines):
731
514
        self.ui_factory.clear_term()
732
 
        for line in lines:
733
 
            self._write(line)
 
515
        self.wrapped_stream.writelines(lines)