~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/ui/__init__.py

merge 2.0 branch rev 4647

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
"""UI abstraction.
18
 
 
19
 
This tells the library how to display things to the user.  Through this
20
 
layer different applications can choose the style of UI.
21
 
 
22
 
At the moment this layer is almost trivial: the application can just
23
 
choose the style of progress bar.
24
 
 
25
 
Set the ui_factory member to define the behaviour.  The default
26
 
displays no output.
 
17
"""Abstraction for interacting with the user.
 
18
 
 
19
Applications can choose different types of UI, and they deal with displaying
 
20
messages or progress to the user, and with gathering different types of input.
 
21
 
 
22
Several levels are supported, and you can also register new factories such as
 
23
for a GUI.
 
24
 
 
25
UIFactory
 
26
    Semi-abstract base class
 
27
 
 
28
SilentUIFactory
 
29
    Produces no output and cannot take any input; useful for programs using
 
30
    bzrlib in batch mode or for programs such as loggerhead.
 
31
 
 
32
CannedInputUIFactory
 
33
    For use in testing; the input values to be returned are provided 
 
34
    at construction.
 
35
 
 
36
TextUIFactory
 
37
    Standard text command-line interface, with stdin, stdout, stderr.
 
38
    May make more or less advanced use of them, eg in drawing progress bars,
 
39
    depending on the detected capabilities of the terminal.
 
40
    GUIs may choose to subclass this so that unimplemented methods fall
 
41
    back to working through the terminal.
27
42
"""
28
43
 
 
44
 
29
45
import os
30
46
import sys
31
47
import warnings
41
57
    trace,
42
58
    )
43
59
""")
 
60
from bzrlib.symbol_versioning import (
 
61
    deprecated_function,
 
62
    deprecated_in,
 
63
    deprecated_method,
 
64
    )
 
65
 
 
66
 
 
67
_valid_boolean_strings = dict(yes=True, no=False,
 
68
                              y=True, n=False,
 
69
                              on=True, off=False,
 
70
                              true=True, false=False)
 
71
_valid_boolean_strings['1'] = True
 
72
_valid_boolean_strings['0'] = False
 
73
 
 
74
 
 
75
def bool_from_string(s, accepted_values=None):
 
76
    """Returns a boolean if the string can be interpreted as such.
 
77
 
 
78
    Interpret case insensitive strings as booleans. The default values
 
79
    includes: 'yes', 'no, 'y', 'n', 'true', 'false', '0', '1', 'on',
 
80
    'off'. Alternative values can be provided with the 'accepted_values'
 
81
    parameter.
 
82
 
 
83
    :param s: A string that should be interpreted as a boolean. It should be of
 
84
        type string or unicode.
 
85
 
 
86
    :param accepted_values: An optional dict with accepted strings as keys and
 
87
        True/False as values. The strings will be tested against a lowered
 
88
        version of 's'.
 
89
 
 
90
    :return: True or False for accepted strings, None otherwise.
 
91
    """
 
92
    if accepted_values is None:
 
93
        accepted_values = _valid_boolean_strings
 
94
    val = None
 
95
    if type(s) in (str, unicode):
 
96
        try:
 
97
            val = accepted_values[s.lower()]
 
98
        except KeyError:
 
99
            pass
 
100
    return val
44
101
 
45
102
 
46
103
class UIFactory(object):
122
179
        """
123
180
        raise NotImplementedError(self.get_boolean)
124
181
 
 
182
    def make_progress_view(self):
 
183
        """Construct a new ProgressView object for this UI.
 
184
 
 
185
        Application code should normally not call this but instead
 
186
        nested_progress_bar().
 
187
        """
 
188
        return NullProgressView()
 
189
 
125
190
    def recommend_upgrade(self,
126
191
        current_format_name,
127
192
        basedir):
146
211
 
147
212
 
148
213
class CLIUIFactory(UIFactory):
149
 
    """Common behaviour for command line UI factories.
150
 
 
151
 
    This is suitable for dumb terminals that can't repaint existing text."""
152
 
 
 
214
    """Deprecated in favor of TextUIFactory."""
 
215
 
 
216
    @deprecated_method(deprecated_in((1, 18, 0)))
153
217
    def __init__(self, stdin=None, stdout=None, stderr=None):
154
218
        UIFactory.__init__(self)
155
219
        self.stdin = stdin or sys.stdin
156
220
        self.stdout = stdout or sys.stdout
157
221
        self.stderr = stderr or sys.stderr
158
222
 
 
223
    _accepted_boolean_strings = dict(y=True, n=False, yes=True, no=False)
 
224
 
159
225
    def get_boolean(self, prompt):
160
 
        # FIXME: make a regexp and handle case variations as well.
161
226
        while True:
162
227
            self.prompt(prompt + "? [y/n]: ")
163
228
            line = self.stdin.readline()
164
 
            if line in ('y\n', 'yes\n'):
165
 
                return True
166
 
            if line in ('n\n', 'no\n'):
167
 
                return False
 
229
            line = line.rstrip('\n')
 
230
            val = bool_from_string(line, self._accepted_boolean_strings)
 
231
            if val is not None:
 
232
                return val
168
233
 
169
234
    def get_non_echoed_password(self):
170
235
        isatty = getattr(self.stdin, 'isatty', None)
234
299
        self.stdout.write(msg + '\n')
235
300
 
236
301
 
237
 
class SilentUIFactory(CLIUIFactory):
 
302
class SilentUIFactory(UIFactory):
238
303
    """A UI Factory which never prints anything.
239
304
 
240
 
    This is the default UI, if another one is never registered.
 
305
    This is the default UI, if another one is never registered by a program
 
306
    using bzrlib, and it's also active for example inside 'bzr serve'.
 
307
 
 
308
    Methods that try to read from the user raise an error; methods that do
 
309
    output do nothing.
241
310
    """
242
311
 
243
312
    def __init__(self):
244
 
        CLIUIFactory.__init__(self)
 
313
        UIFactory.__init__(self)
 
314
 
 
315
    def note(self, msg):
 
316
        pass
 
317
 
 
318
    def get_username(self, prompt, **kwargs):
 
319
        return None
 
320
 
 
321
 
 
322
class CannedInputUIFactory(SilentUIFactory):
 
323
    """A silent UI that return canned input."""
 
324
 
 
325
    def __init__(self, responses):
 
326
        self.responses = responses
 
327
 
 
328
    def __repr__(self):
 
329
        return "%s(%r)" % (self.__class__.__name__, self.responses)
 
330
 
 
331
    def get_boolean(self, prompt):
 
332
        return self.responses.pop(0)
245
333
 
246
334
    def get_password(self, prompt='', **kwargs):
247
 
        return None
248
 
 
249
 
    def get_username(self, prompt='', **kwargs):
250
 
        return None
251
 
 
252
 
    def prompt(self, prompt, **kwargs):
253
 
        pass
254
 
 
255
 
    def note(self, msg):
256
 
        pass
257
 
 
258
 
 
 
335
        return self.responses.pop(0)
 
336
 
 
337
    def get_username(self, prompt, **kwargs):
 
338
        return self.responses.pop(0)
 
339
    
 
340
    def assert_all_input_consumed(self):
 
341
        if self.responses:
 
342
            raise AssertionError("expected all input in %r to be consumed"
 
343
                % (self,))
 
344
 
 
345
 
 
346
@deprecated_function(deprecated_in((1, 18, 0)))
259
347
def clear_decorator(func, *args, **kwargs):
260
348
    """Decorator that clears the term"""
261
349
    ui_factory.clear_term()
263
351
 
264
352
 
265
353
ui_factory = SilentUIFactory()
266
 
"""IMPORTANT: never import this symbol directly. ONLY ever access it as
267
 
ui.ui_factory."""
 
354
# IMPORTANT: never import this symbol directly. ONLY ever access it as
 
355
# ui.ui_factory, so that you refer to the current value.
268
356
 
269
357
 
270
358
def make_ui_for_terminal(stdin, stdout, stderr):
271
359
    """Construct and return a suitable UIFactory for a text mode program.
272
 
 
273
 
    If stdout is a smart terminal, this gets a smart UIFactory with
274
 
    progress indicators, etc.  If it's a dumb terminal, just plain text output.
275
360
    """
276
 
    cls = None
277
 
    isatty = getattr(stdin, 'isatty', None)
278
 
    if isatty is None:
279
 
        cls = CLIUIFactory
280
 
    elif not isatty():
281
 
        cls = CLIUIFactory
282
 
    elif os.environ.get('TERM') in ('dumb', ''):
283
 
        # e.g. emacs compile window
284
 
        cls = CLIUIFactory
285
 
    # User may know better, otherwise default to TextUIFactory
286
 
    if (   os.environ.get('BZR_USE_TEXT_UI', None) is not None
287
 
        or cls is None):
288
 
        from bzrlib.ui.text import TextUIFactory
289
 
        cls = TextUIFactory
290
 
    return cls(stdin=stdin, stdout=stdout, stderr=stderr)
 
361
    # this is now always TextUIFactory, which in turn decides whether it
 
362
    # should display progress bars etc
 
363
    from bzrlib.ui.text import TextUIFactory
 
364
    return TextUIFactory(stdin, stdout, stderr)
 
365
 
 
366
 
 
367
class NullProgressView(object):
 
368
    """Soak up and ignore progress information."""
 
369
 
 
370
    def clear(self):
 
371
        pass
 
372
 
 
373
    def show_progress(self, task):
 
374
        pass
 
375
 
 
376
    def show_transport_activity(self, transport, direction, byte_count):
 
377
        pass