~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/ui/__init__.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-08-13 14:20:03 UTC
  • mfrom: (4599.2.4 windows-installer)
  • Revision ID: pqm@pqm.ubuntu.com-20090813142003-3x748ymw3avzmme7
(jam) Updates to the windows installers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
22
22
Several levels are supported, and you can also register new factories such as
23
23
for a GUI.
24
24
 
25
 
bzrlib.ui.UIFactory
 
25
UIFactory
26
26
    Semi-abstract base class
27
27
 
28
 
bzrlib.ui.SilentUIFactory
 
28
SilentUIFactory
29
29
    Produces no output and cannot take any input; useful for programs using
30
30
    bzrlib in batch mode or for programs such as loggerhead.
31
31
 
32
 
bzrlib.ui.CannedInputUIFactory
 
32
CannedInputUIFactory
33
33
    For use in testing; the input values to be returned are provided 
34
34
    at construction.
35
35
 
36
 
bzrlib.ui.text.TextUIFactory
 
36
TextUIFactory
37
37
    Standard text command-line interface, with stdin, stdout, stderr.
38
38
    May make more or less advanced use of them, eg in drawing progress bars,
39
39
    depending on the detected capabilities of the terminal.
105
105
 
106
106
    This tells the library how to display things to the user.  Through this
107
107
    layer different applications can choose the style of UI.
108
 
 
109
 
    UI Factories are also context managers, for some syntactic sugar some users
110
 
    need.
111
 
 
112
 
    :ivar suppressed_warnings: Identifiers for user warnings that should 
113
 
        no be emitted.
114
108
    """
115
109
 
116
 
    _user_warning_templates = dict(
117
 
        cross_format_fetch=("Doing on-the-fly conversion from "
118
 
            "%(from_format)s to %(to_format)s.\n"
119
 
            "This may take some time. Upgrade the repositories to the "
120
 
            "same format for better performance."
121
 
            )
122
 
        )
123
 
 
124
110
    def __init__(self):
125
111
        self._task_stack = []
126
 
        self.suppressed_warnings = set()
127
 
        self._quiet = False
128
 
 
129
 
    def __enter__(self):
130
 
        """Context manager entry support.
131
 
 
132
 
        Override in a concrete factory class if initialisation before use is
133
 
        needed.
134
 
        """
135
 
        return self # This is bound to the 'as' clause in a with statement.
136
 
 
137
 
    def __exit__(self, exc_type, exc_val, exc_tb):
138
 
        """Context manager exit support.
139
 
 
140
 
        Override in a concrete factory class if more cleanup than a simple
141
 
        self.clear_term() is needed when the UIFactory is finished with.
142
 
        """
143
 
        self.clear_term()
144
 
        return False # propogate exceptions.
145
 
 
146
 
    def be_quiet(self, state):
147
 
        """Tell the UI to be more quiet, or not.
148
 
 
149
 
        Typically this suppresses progress bars; the application may also look
150
 
        at ui_factory.is_quiet().
151
 
        """
152
 
        self._quiet = state
153
112
 
154
113
    def get_password(self, prompt='', **kwargs):
155
114
        """Prompt the user for a password.
166
125
        """
167
126
        raise NotImplementedError(self.get_password)
168
127
 
169
 
    def is_quiet(self):
170
 
        return self._quiet
171
 
 
172
 
    def make_output_stream(self, encoding=None, encoding_type=None):
173
 
        """Get a stream for sending out bulk text data.
174
 
 
175
 
        This is used for commands that produce bulk text, such as log or diff
176
 
        output, as opposed to user interaction.  This should work even for
177
 
        non-interactive user interfaces.  Typically this goes to a decorated
178
 
        version of stdout, but in a GUI it might be appropriate to send it to a 
179
 
        window displaying the text.
180
 
     
181
 
        :param encoding: Unicode encoding for output; if not specified 
182
 
            uses the configured 'output_encoding' if any; otherwise the 
183
 
            terminal encoding. 
184
 
            (See get_terminal_encoding.)
185
 
 
186
 
        :param encoding_type: How to handle encoding errors:
187
 
            replace/strict/escape/exact.  Default is replace.
188
 
        """
189
 
        # XXX: is the caller supposed to close the resulting object?
190
 
        if encoding is None:
191
 
            from bzrlib import config
192
 
            encoding = config.GlobalConfig().get_user_option(
193
 
                'output_encoding')
194
 
        if encoding is None:
195
 
            encoding = osutils.get_terminal_encoding(trace=True)
196
 
        if encoding_type is None:
197
 
            encoding_type = 'replace'
198
 
        out_stream = self._make_output_stream_explicit(encoding, encoding_type)
199
 
        return out_stream
200
 
 
201
 
    def _make_output_stream_explicit(self, encoding, encoding_type):
202
 
        raise NotImplementedError("%s doesn't support make_output_stream"
203
 
            % (self.__class__.__name__))
204
 
 
205
128
    def nested_progress_bar(self):
206
129
        """Return a nested progress bar.
207
130
 
220
143
        if not self._task_stack:
221
144
            warnings.warn("%r finished but nothing is active"
222
145
                % (task,))
223
 
        if task in self._task_stack:
224
 
            self._task_stack.remove(task)
 
146
        elif task != self._task_stack[-1]:
 
147
            warnings.warn("%r is not the active task %r"
 
148
                % (task, self._task_stack[-1]))
225
149
        else:
226
 
            warnings.warn("%r is not in active stack %r"
227
 
                % (task, self._task_stack))
 
150
            del self._task_stack[-1]
228
151
        if not self._task_stack:
229
152
            self._progress_all_finished()
230
153
 
247
170
        """
248
171
        pass
249
172
 
250
 
    def format_user_warning(self, warning_id, message_args):
251
 
        try:
252
 
            template = self._user_warning_templates[warning_id]
253
 
        except KeyError:
254
 
            fail = "failed to format warning %r, %r" % (warning_id, message_args)
255
 
            warnings.warn(fail)   # so tests will fail etc
256
 
            return fail
257
 
        try:
258
 
            return template % message_args
259
 
        except ValueError, e:
260
 
            fail = "failed to format warning %r, %r: %s" % (
261
 
                warning_id, message_args, e)
262
 
            warnings.warn(fail)   # so tests will fail etc
263
 
            return fail
264
 
 
265
173
    def get_boolean(self, prompt):
266
174
        """Get a boolean question answered from the user.
267
175
 
271
179
        """
272
180
        raise NotImplementedError(self.get_boolean)
273
181
 
274
 
    def get_integer(self, prompt):
275
 
        """Get an integer from the user.
276
 
 
277
 
        :param prompt: a message to prompt the user with. Could be a multi-line
278
 
            prompt but without a terminating \n.
279
 
 
280
 
        :return: A signed integer.
281
 
        """
282
 
        raise NotImplementedError(self.get_integer)
283
 
 
284
182
    def make_progress_view(self):
285
183
        """Construct a new ProgressView object for this UI.
286
184
 
292
190
    def recommend_upgrade(self,
293
191
        current_format_name,
294
192
        basedir):
295
 
        # XXX: this should perhaps be in the TextUIFactory and the default can do
 
193
        # this should perhaps be in the TextUIFactory and the default can do
296
194
        # nothing
297
 
        #
298
 
        # XXX: Change to show_user_warning - that will accomplish the previous
299
 
        # xxx. -- mbp 2010-02-25
300
195
        trace.warning("%s is deprecated "
301
196
            "and a better format is available.\n"
302
197
            "It is recommended that you upgrade by "
313
208
        """
314
209
        pass
315
210
 
316
 
    def log_transport_activity(self, display=False):
317
 
        """Write out whatever transport activity has been measured.
318
 
 
319
 
        Implementations are allowed to do nothing, but it is useful if they can
320
 
        write a line to the log file.
321
 
 
322
 
        :param display: If False, only log to disk, if True also try to display
323
 
            a message to the user.
324
 
        :return: None
325
 
        """
326
 
        # Default implementation just does nothing
327
 
        pass
328
 
 
329
 
    def show_user_warning(self, warning_id, **message_args):
330
 
        """Show a warning to the user.
331
 
 
332
 
        This is specifically for things that are under the user's control (eg
333
 
        outdated formats), not for internal program warnings like deprecated
334
 
        APIs.
335
 
 
336
 
        This can be overridden by UIFactory subclasses to show it in some 
337
 
        appropriate way; the default UIFactory is noninteractive and does
338
 
        nothing.  format_user_warning maps it to a string, though other
339
 
        presentations can be used for particular UIs.
340
 
 
341
 
        :param warning_id: An identifier like 'cross_format_fetch' used to 
342
 
            check if the message is suppressed and to look up the string.
343
 
        :param message_args: Arguments to be interpolated into the message.
344
 
        """
345
 
        pass
346
 
 
347
 
    def show_error(self, msg):
348
 
        """Show an error message (not an exception) to the user.
349
 
        
350
 
        The message should not have an error prefix or trailing newline.  That
351
 
        will be added by the factory if appropriate.
352
 
        """
353
 
        raise NotImplementedError(self.show_error)
354
 
 
355
 
    def show_message(self, msg):
356
 
        """Show a message to the user."""
357
 
        raise NotImplementedError(self.show_message)
358
 
 
359
 
    def show_warning(self, msg):
360
 
        """Show a warning to the user."""
361
 
        raise NotImplementedError(self.show_warning)
362
 
 
363
 
    def warn_cross_format_fetch(self, from_format, to_format):
364
 
        """Warn about a potentially slow cross-format transfer.
365
 
        
366
 
        This is deprecated in favor of show_user_warning, but retained for api
367
 
        compatibility in 2.0 and 2.1.
368
 
        """
369
 
        self.show_user_warning('cross_format_fetch', from_format=from_format,
370
 
            to_format=to_format)
371
 
 
372
 
    def warn_experimental_format_fetch(self, inter):
373
 
        """Warn about fetching into experimental repository formats."""
374
 
        if inter.target._format.experimental:
375
 
            trace.warning("Fetching into experimental format %s.\n"
376
 
                "This format may be unreliable or change in the future "
377
 
                "without an upgrade path.\n" % (inter.target._format,))
 
211
 
 
212
 
 
213
class CLIUIFactory(UIFactory):
 
214
    """Deprecated in favor of TextUIFactory."""
 
215
 
 
216
    @deprecated_method(deprecated_in((1, 18, 0)))
 
217
    def __init__(self, stdin=None, stdout=None, stderr=None):
 
218
        UIFactory.__init__(self)
 
219
        self.stdin = stdin or sys.stdin
 
220
        self.stdout = stdout or sys.stdout
 
221
        self.stderr = stderr or sys.stderr
 
222
 
 
223
    _accepted_boolean_strings = dict(y=True, n=False, yes=True, no=False)
 
224
 
 
225
    def get_boolean(self, prompt):
 
226
        while True:
 
227
            self.prompt(prompt + "? [y/n]: ")
 
228
            line = self.stdin.readline()
 
229
            line = line.rstrip('\n')
 
230
            val = bool_from_string(line, self._accepted_boolean_strings)
 
231
            if val is not None:
 
232
                return val
 
233
 
 
234
    def get_non_echoed_password(self):
 
235
        isatty = getattr(self.stdin, 'isatty', None)
 
236
        if isatty is not None and isatty():
 
237
            # getpass() ensure the password is not echoed and other
 
238
            # cross-platform niceties
 
239
            password = getpass.getpass('')
 
240
        else:
 
241
            # echo doesn't make sense without a terminal
 
242
            password = self.stdin.readline()
 
243
            if not password:
 
244
                password = None
 
245
            elif password[-1] == '\n':
 
246
                password = password[:-1]
 
247
        return password
 
248
 
 
249
    def get_password(self, prompt='', **kwargs):
 
250
        """Prompt the user for a password.
 
251
 
 
252
        :param prompt: The prompt to present the user
 
253
        :param kwargs: Arguments which will be expanded into the prompt.
 
254
                       This lets front ends display different things if
 
255
                       they so choose.
 
256
        :return: The password string, return None if the user
 
257
                 canceled the request.
 
258
        """
 
259
        prompt += ': '
 
260
        self.prompt(prompt, **kwargs)
 
261
        # There's currently no way to say 'i decline to enter a password'
 
262
        # as opposed to 'my password is empty' -- does it matter?
 
263
        return self.get_non_echoed_password()
 
264
 
 
265
    def get_username(self, prompt, **kwargs):
 
266
        """Prompt the user for a username.
 
267
 
 
268
        :param prompt: The prompt to present the user
 
269
        :param kwargs: Arguments which will be expanded into the prompt.
 
270
                       This lets front ends display different things if
 
271
                       they so choose.
 
272
        :return: The username string, return None if the user
 
273
                 canceled the request.
 
274
        """
 
275
        prompt += ': '
 
276
        self.prompt(prompt, **kwargs)
 
277
        username = self.stdin.readline()
 
278
        if not username:
 
279
            username = None
 
280
        elif username[-1] == '\n':
 
281
            username = username[:-1]
 
282
        return username
 
283
 
 
284
    def prompt(self, prompt, **kwargs):
 
285
        """Emit prompt on the CLI.
 
286
        
 
287
        :param kwargs: Dictionary of arguments to insert into the prompt,
 
288
            to allow UIs to reformat the prompt.
 
289
        """
 
290
        if kwargs:
 
291
            # See <https://launchpad.net/bugs/365891>
 
292
            prompt = prompt % kwargs
 
293
        prompt = prompt.encode(osutils.get_terminal_encoding(), 'replace')
 
294
        self.clear_term()
 
295
        self.stderr.write(prompt)
 
296
 
 
297
    def note(self, msg):
 
298
        """Write an already-formatted message."""
 
299
        self.stdout.write(msg + '\n')
378
300
 
379
301
 
380
302
class SilentUIFactory(UIFactory):
396
318
    def get_username(self, prompt, **kwargs):
397
319
        return None
398
320
 
399
 
    def _make_output_stream_explicit(self, encoding, encoding_type):
400
 
        return NullOutputStream(encoding)
401
 
 
402
 
    def show_error(self, msg):
403
 
        pass
404
 
 
405
 
    def show_message(self, msg):
406
 
        pass
407
 
 
408
 
    def show_warning(self, msg):
409
 
        pass
410
 
 
411
321
 
412
322
class CannedInputUIFactory(SilentUIFactory):
413
323
    """A silent UI that return canned input."""
421
331
    def get_boolean(self, prompt):
422
332
        return self.responses.pop(0)
423
333
 
424
 
    def get_integer(self, prompt):
425
 
        return self.responses.pop(0)
426
 
 
427
334
    def get_password(self, prompt='', **kwargs):
428
335
        return self.responses.pop(0)
429
336
 
430
337
    def get_username(self, prompt, **kwargs):
431
338
        return self.responses.pop(0)
432
 
 
 
339
    
433
340
    def assert_all_input_consumed(self):
434
341
        if self.responses:
435
342
            raise AssertionError("expected all input in %r to be consumed"
436
343
                % (self,))
437
344
 
438
345
 
 
346
@deprecated_function(deprecated_in((1, 18, 0)))
 
347
def clear_decorator(func, *args, **kwargs):
 
348
    """Decorator that clears the term"""
 
349
    ui_factory.clear_term()
 
350
    func(*args, **kwargs)
 
351
 
 
352
 
439
353
ui_factory = SilentUIFactory()
440
354
# IMPORTANT: never import this symbol directly. ONLY ever access it as
441
355
# ui.ui_factory, so that you refer to the current value.
461
375
 
462
376
    def show_transport_activity(self, transport, direction, byte_count):
463
377
        pass
464
 
 
465
 
    def log_transport_activity(self, display=False):
466
 
        pass
467
 
 
468
 
 
469
 
class NullOutputStream(object):
470
 
    """Acts like a file, but discard all output."""
471
 
 
472
 
    def __init__(self, encoding):
473
 
        self.encoding = encoding
474
 
 
475
 
    def write(self, data):
476
 
        pass
477
 
 
478
 
    def writelines(self, data):
479
 
        pass
480
 
 
481
 
    def close(self):
482
 
        pass