~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/ui/text.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-16 19:18:39 UTC
  • mto: This revision was merged to the branch mainline in revision 6391.
  • Revision ID: jelmer@samba.org-20111216191839-eg681lxqibi1qxu1
Fix remaining tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
18
18
"""Text UI, write output to the console.
19
19
"""
20
20
 
21
 
import codecs
22
 
import getpass
23
21
import os
24
22
import sys
25
23
import time
26
 
import warnings
27
24
 
28
25
from bzrlib.lazy_import import lazy_import
29
26
lazy_import(globals(), """
 
27
import codecs
 
28
import getpass
 
29
import warnings
 
30
 
30
31
from bzrlib import (
31
32
    debug,
32
33
    progress,
33
34
    osutils,
34
 
    symbol_versioning,
35
35
    trace,
36
36
    )
37
37
 
43
43
    )
44
44
 
45
45
 
 
46
class _ChooseUI(object):
 
47
 
 
48
    """ Helper class for choose implementation.
 
49
    """
 
50
 
 
51
    def __init__(self, ui, msg, choices, default):
 
52
        self.ui = ui
 
53
        self._setup_mode()
 
54
        self._build_alternatives(msg, choices, default)
 
55
 
 
56
    def _setup_mode(self):
 
57
        """Setup input mode (line-based, char-based) and echo-back.
 
58
 
 
59
        Line-based input is used if the BZR_TEXTUI_INPUT environment
 
60
        variable is set to 'line-based', or if there is no controlling
 
61
        terminal.
 
62
        """
 
63
        if os.environ.get('BZR_TEXTUI_INPUT') != 'line-based' and \
 
64
           self.ui.stdin == sys.stdin and self.ui.stdin.isatty():
 
65
            self.line_based = False
 
66
            self.echo_back = True
 
67
        else:
 
68
            self.line_based = True
 
69
            self.echo_back = not self.ui.stdin.isatty()
 
70
 
 
71
    def _build_alternatives(self, msg, choices, default):
 
72
        """Parse choices string.
 
73
 
 
74
        Setup final prompt and the lists of choices and associated
 
75
        shortcuts.
 
76
        """
 
77
        index = 0
 
78
        help_list = []
 
79
        self.alternatives = {}
 
80
        choices = choices.split('\n')
 
81
        if default is not None and default not in range(0, len(choices)):
 
82
            raise ValueError("invalid default index")
 
83
        for c in choices:
 
84
            name = c.replace('&', '').lower()
 
85
            choice = (name, index)
 
86
            if name in self.alternatives:
 
87
                raise ValueError("duplicated choice: %s" % name)
 
88
            self.alternatives[name] = choice
 
89
            shortcut = c.find('&')
 
90
            if -1 != shortcut and (shortcut + 1) < len(c):
 
91
                help = c[:shortcut]
 
92
                help += '[' + c[shortcut + 1] + ']'
 
93
                help += c[(shortcut + 2):]
 
94
                shortcut = c[shortcut + 1]
 
95
            else:
 
96
                c = c.replace('&', '')
 
97
                shortcut = c[0]
 
98
                help = '[%s]%s' % (shortcut, c[1:])
 
99
            shortcut = shortcut.lower()
 
100
            if shortcut in self.alternatives:
 
101
                raise ValueError("duplicated shortcut: %s" % shortcut)
 
102
            self.alternatives[shortcut] = choice
 
103
            # Add redirections for default.
 
104
            if index == default:
 
105
                self.alternatives[''] = choice
 
106
                self.alternatives['\r'] = choice
 
107
            help_list.append(help)
 
108
            index += 1
 
109
 
 
110
        self.prompt = u'%s (%s): ' % (msg, ', '.join(help_list))
 
111
 
 
112
    def _getline(self):
 
113
        line = self.ui.stdin.readline()
 
114
        if '' == line:
 
115
            raise EOFError
 
116
        return line.strip()
 
117
 
 
118
    def _getchar(self):
 
119
        char = osutils.getchar()
 
120
        if char == chr(3): # INTR
 
121
            raise KeyboardInterrupt
 
122
        if char == chr(4): # EOF (^d, C-d)
 
123
            raise EOFError
 
124
        return char
 
125
 
 
126
    def interact(self):
 
127
        """Keep asking the user until a valid choice is made.
 
128
        """
 
129
        if self.line_based:
 
130
            getchoice = self._getline
 
131
        else:
 
132
            getchoice = self._getchar
 
133
        iter = 0
 
134
        while True:
 
135
            iter += 1
 
136
            if 1 == iter or self.line_based:
 
137
                self.ui.prompt(self.prompt)
 
138
            try:
 
139
                choice = getchoice()
 
140
            except EOFError:
 
141
                self.ui.stderr.write('\n')
 
142
                return None
 
143
            except KeyboardInterrupt:
 
144
                self.ui.stderr.write('\n')
 
145
                raise KeyboardInterrupt
 
146
            choice = choice.lower()
 
147
            if choice not in self.alternatives:
 
148
                # Not a valid choice, keep on asking.
 
149
                continue
 
150
            name, index = self.alternatives[choice]
 
151
            if self.echo_back:
 
152
                self.ui.stderr.write(name + '\n')
 
153
            return index
 
154
 
 
155
 
46
156
class TextUIFactory(UIFactory):
47
157
    """A UI factory for Text user interefaces."""
48
158
 
61
171
        # paints progress, network activity, etc
62
172
        self._progress_view = self.make_progress_view()
63
173
 
 
174
    def choose(self, msg, choices, default=None):
 
175
        """Prompt the user for a list of alternatives.
 
176
 
 
177
        Support both line-based and char-based editing.
 
178
 
 
179
        In line-based mode, both the shortcut and full choice name are valid
 
180
        answers, e.g. for choose('prompt', '&yes\n&no'): 'y', ' Y ', ' yes',
 
181
        'YES ' are all valid input lines for choosing 'yes'.
 
182
 
 
183
        An empty line, when in line-based mode, or pressing enter in char-based
 
184
        mode will select the default choice (if any).
 
185
 
 
186
        Choice is echoed back if:
 
187
        - input is char-based; which means a controlling terminal is available,
 
188
          and osutils.getchar is used
 
189
        - input is line-based, and no controlling terminal is available
 
190
        """
 
191
 
 
192
        choose_ui = _ChooseUI(self, msg, choices, default)
 
193
        return choose_ui.interact()
 
194
 
64
195
    def be_quiet(self, state):
65
196
        if state and not self._quiet:
66
197
            self.clear_term()
78
209
        # to clear it.  We might need to separately check for the case of
79
210
        self._progress_view.clear()
80
211
 
81
 
    def get_boolean(self, prompt):
82
 
        while True:
83
 
            self.prompt(prompt + "? [y/n]: ")
84
 
            line = self.stdin.readline().lower()
85
 
            if line in ('y\n', 'yes\n'):
86
 
                return True
87
 
            elif line in ('n\n', 'no\n'):
88
 
                return False
89
 
            elif line in ('', None):
90
 
                # end-of-file; possibly should raise an error here instead
91
 
                return None
92
 
 
93
212
    def get_integer(self, prompt):
94
213
        while True:
95
214
            self.prompt(prompt)
114
233
                password = password[:-1]
115
234
        return password
116
235
 
117
 
    def get_password(self, prompt='', **kwargs):
 
236
    def get_password(self, prompt=u'', **kwargs):
118
237
        """Prompt the user for a password.
119
238
 
120
239
        :param prompt: The prompt to present the user
198
317
        :param kwargs: Dictionary of arguments to insert into the prompt,
199
318
            to allow UIs to reformat the prompt.
200
319
        """
 
320
        if type(prompt) != unicode:
 
321
            raise ValueError("prompt %r not a unicode string" % prompt)
201
322
        if kwargs:
202
323
            # See <https://launchpad.net/bugs/365891>
203
324
            prompt = prompt % kwargs
204
325
        prompt = prompt.encode(osutils.get_terminal_encoding(), 'replace')
205
326
        self.clear_term()
 
327
        self.stdout.flush()
206
328
        self.stderr.write(prompt)
207
329
 
208
330
    def report_transport_activity(self, transport, byte_count, direction):