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
17
"""Abstraction for interacting with the user.
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.
19
This tells the library how to display things to the user. Through this
20
layer different applications can choose the style of UI.
22
22
Several levels are supported, and you can also register new factories such as
26
26
Semi-abstract base class
28
bzrlib.ui.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.
32
bzrlib.ui.CannedInputUIFactory
33
For use in testing; the input values to be returned are provided
36
bzrlib.ui.text.TextUIFactory
37
33
Standard text command-line interface, with stdin, stdout, stderr.
38
34
May make more or less advanced use of them, eg in drawing progress bars,
39
35
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.
38
# Previously, there was CLIUIFactory for dumb terminals, and TextUIFactory for
39
# those on smarter terminals. However, there are actually a few independent
40
# variables so simple subclassing doesn't make sense. The user may or may not
41
# want progress bars, but even if they want them off they may want a rich
42
# interface in other ways. And on the other hand input may be redirected from
43
# a file so stdin is not a terminal, but we should still try to read input
44
# from it and display progress bars etc. Therefore if we're doing a text UI
45
# at all, we just use TextUIFactory and it turns some features on or off
46
# depending on the detected settings and capabilities.
48
# We also use this factory for most blackbox tests, but typically with non-tty
49
# streams and TERM=dumb. For api tests, SilentUIFactory is probably
52
# GUIs may actually choose to subclass TextUIFactory, so unimplemented methods
53
# fall back to working through the terminal.
55
# SilentUIFactory used to read input from stdin, but not print anything, which
56
# now seems like an unhelpful combination. So it will now just fail if asked
67
_valid_boolean_strings = dict(yes=True, no=False,
70
true=True, false=False)
71
_valid_boolean_strings['1'] = True
72
_valid_boolean_strings['0'] = False
75
def bool_from_string(s, accepted_values=None):
76
"""Returns a boolean if the string can be interpreted as such.
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'
83
:param s: A string that should be interpreted as a boolean. It should be of
84
type string or unicode.
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
90
:return: True or False for accepted strings, None otherwise.
92
if accepted_values is None:
93
accepted_values = _valid_boolean_strings
95
if type(s) in (str, unicode):
97
val = accepted_values[s.lower()]
103
81
class UIFactory(object):
104
82
"""UI abstraction.
106
84
This tells the library how to display things to the user. Through this
107
85
layer different applications can choose the style of UI.
109
:ivar suppressed_warnings: Identifiers for user warnings that should
113
_user_warning_templates = dict(
114
cross_format_fetch=("Doing on-the-fly conversion from "
115
"%(from_format)s to %(to_format)s.\n"
116
"This may take some time. Upgrade the repositories to the "
117
"same format for better performance."
121
88
def __init__(self):
122
89
self._task_stack = []
123
self.suppressed_warnings = set()
126
def be_quiet(self, state):
127
"""Tell the UI to be more quiet, or not.
129
Typically this suppresses progress bars; the application may also look
130
at ui_factory.is_quiet().
134
91
def get_password(self, prompt='', **kwargs):
135
92
"""Prompt the user for a password.
147
104
raise NotImplementedError(self.get_password)
152
def make_output_stream(self, encoding=None, encoding_type=None):
153
"""Get a stream for sending out bulk text data.
155
This is used for commands that produce bulk text, such as log or diff
156
output, as opposed to user interaction. This should work even for
157
non-interactive user interfaces. Typically this goes to a decorated
158
version of stdout, but in a GUI it might be appropriate to send it to a
159
window displaying the text.
161
:param encoding: Unicode encoding for output; default is the
162
terminal encoding, which may be different from the user encoding.
163
(See get_terminal_encoding.)
165
:param encoding_type: How to handle encoding errors:
166
replace/strict/escape/exact. Default is replace.
168
# XXX: is the caller supposed to close the resulting object?
170
encoding = osutils.get_terminal_encoding()
171
if encoding_type is None:
172
encoding_type = 'replace'
173
out_stream = self._make_output_stream_explicit(encoding, encoding_type)
176
def _make_output_stream_explicit(self, encoding, encoding_type):
177
raise NotImplementedError("%s doesn't support make_output_stream"
178
% (self.__class__.__name__))
180
106
def nested_progress_bar(self):
181
107
"""Return a nested progress bar.
225
def format_user_warning(self, warning_id, message_args):
227
template = self._user_warning_templates[warning_id]
229
fail = "failed to format warning %r, %r" % (warning_id, message_args)
230
warnings.warn(fail) # so tests will fail etc
233
return template % message_args
234
except ValueError, e:
235
fail = "failed to format warning %r, %r: %s" % (
236
warning_id, message_args, e)
237
warnings.warn(fail) # so tests will fail etc
240
151
def get_boolean(self, prompt):
241
152
"""Get a boolean question answered from the user.
291
def log_transport_activity(self, display=False):
292
"""Write out whatever transport activity has been measured.
294
Implementations are allowed to do nothing, but it is useful if they can
295
write a line to the log file.
297
:param display: If False, only log to disk, if True also try to display
298
a message to the user.
301
# Default implementation just does nothing
304
def show_user_warning(self, warning_id, **message_args):
305
"""Show a warning to the user.
307
This is specifically for things that are under the user's control (eg
308
outdated formats), not for internal program warnings like deprecated
311
This can be overridden by UIFactory subclasses to show it in some
312
appropriate way; the default UIFactory is noninteractive and does
313
nothing. format_user_warning maps it to a string, though other
314
presentations can be used for particular UIs.
316
:param warning_id: An identifier like 'cross_format_fetch' used to
317
check if the message is suppressed and to look up the string.
318
:param message_args: Arguments to be interpolated into the message.
322
def show_error(self, msg):
323
"""Show an error message (not an exception) to the user.
325
The message should not have an error prefix or trailing newline. That
326
will be added by the factory if appropriate.
328
raise NotImplementedError(self.show_error)
330
def show_message(self, msg):
331
"""Show a message to the user."""
332
raise NotImplementedError(self.show_message)
334
def show_warning(self, msg):
335
"""Show a warning to the user."""
336
raise NotImplementedError(self.show_warning)
338
def warn_cross_format_fetch(self, from_format, to_format):
339
"""Warn about a potentially slow cross-format transfer.
341
This is deprecated in favor of show_user_warning, but retained for api
342
compatibility in 2.0 and 2.1.
344
self.show_user_warning('cross_format_fetch', from_format=from_format,
347
def warn_experimental_format_fetch(self, inter):
348
"""Warn about fetching into experimental repository formats."""
349
if inter.target._format.experimental:
350
trace.warning("Fetching into experimental format %s.\n"
351
"This format may be unreliable or change in the future "
352
"without an upgrade path.\n" % (inter.target._format,))
191
class CLIUIFactory(UIFactory):
192
"""Deprecated in favor of TextUIFactory."""
194
@deprecated_method(deprecated_in((1, 17, 0)))
195
def __init__(self, stdin=None, stdout=None, stderr=None):
196
UIFactory.__init__(self)
197
self.stdin = stdin or sys.stdin
198
self.stdout = stdout or sys.stdout
199
self.stderr = stderr or sys.stderr
356
202
class SilentUIFactory(UIFactory):
357
203
"""A UI Factory which never prints anything.
359
This is the default UI, if another one is never registered by a program
360
using bzrlib, and it's also active for example inside 'bzr serve'.
362
Methods that try to read from the user raise an error; methods that do
205
This is the default UI, if another one is never registered.
366
208
def __init__(self):
369
211
def note(self, msg):
372
def get_username(self, prompt, **kwargs):
375
def _make_output_stream_explicit(self, encoding, encoding_type):
376
return NullOutputStream(encoding)
378
def show_error(self, msg):
381
def show_message(self, msg):
384
def show_warning(self, msg):
388
215
class CannedInputUIFactory(SilentUIFactory):
389
216
"""A silent UI that return canned input."""
391
218
def __init__(self, responses):
392
219
self.responses = responses
395
return "%s(%r)" % (self.__class__.__name__, self.responses)
397
221
def get_boolean(self, prompt):
398
222
return self.responses.pop(0)
400
def get_integer(self, prompt):
401
return self.responses.pop(0)
403
def get_password(self, prompt='', **kwargs):
404
return self.responses.pop(0)
406
def get_username(self, prompt, **kwargs):
407
return self.responses.pop(0)
409
def assert_all_input_consumed(self):
411
raise AssertionError("expected all input in %r to be consumed"
225
@deprecated_function(deprecated_in((1, 17, 0)))
226
def clear_decorator(func, *args, **kwargs):
227
"""Decorator that clears the term"""
228
ui_factory.clear_term()
229
func(*args, **kwargs)
415
232
ui_factory = SilentUIFactory()