~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: 2011-04-02 00:59:52 UTC
  • mfrom: (5622.4.4 uninstall-hook)
  • Revision ID: pqm@pqm.ubuntu.com-20110402005952-kxcwbwdk6jagtfwm
(jelmer) Add Hooks.uninstall_named_hook(). (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
100
100
    return val
101
101
 
102
102
 
 
103
class ConfirmationUserInterfacePolicy(object):
 
104
    """Wrapper for a UIFactory that allows or denies all confirmed actions."""
 
105
 
 
106
    def __init__(self, wrapped_ui, default_answer, specific_answers):
 
107
        """Generate a proxy UI that does no confirmations.
 
108
 
 
109
        :param wrapped_ui: Underlying UIFactory.
 
110
        :param default_answer: Bool for whether requests for
 
111
            confirmation from the user should be noninteractively accepted or
 
112
            denied.
 
113
        :param specific_answers: Map from confirmation_id to bool answer.
 
114
        """
 
115
        self.wrapped_ui = wrapped_ui
 
116
        self.default_answer = default_answer
 
117
        self.specific_answers = specific_answers
 
118
 
 
119
    def __getattr__(self, name):
 
120
        return getattr(self.wrapped_ui, name)
 
121
 
 
122
    def __repr__(self):
 
123
        return '%s(%r, %r, %r)' % (
 
124
            self.__class__.__name__,
 
125
            self.wrapped_ui,
 
126
            self.default_answer, 
 
127
            self.specific_answers)
 
128
 
 
129
    def confirm_action(self, prompt, confirmation_id, prompt_kwargs):
 
130
        if confirmation_id in self.specific_answers:
 
131
            return self.specific_answers[confirmation_id]
 
132
        elif self.default_answer is not None:
 
133
            return self.default_answer
 
134
        else:
 
135
            return self.wrapped_ui.confirm_action(
 
136
                prompt, confirmation_id, prompt_kwargs)
 
137
 
 
138
 
103
139
class UIFactory(object):
104
140
    """UI abstraction.
105
141
 
106
142
    This tells the library how to display things to the user.  Through this
107
143
    layer different applications can choose the style of UI.
 
144
 
 
145
    UI Factories are also context managers, for some syntactic sugar some users
 
146
    need.
 
147
 
 
148
    :ivar suppressed_warnings: Identifiers for user warnings that should 
 
149
        no be emitted.
108
150
    """
109
151
 
 
152
    _user_warning_templates = dict(
 
153
        cross_format_fetch=("Doing on-the-fly conversion from "
 
154
            "%(from_format)s to %(to_format)s.\n"
 
155
            "This may take some time. Upgrade the repositories to the "
 
156
            "same format for better performance."
 
157
            ),
 
158
        recommend_upgrade=("%(current_format_name)s is deprecated "
 
159
            "and a better format is available.\n"
 
160
            "It is recommended that you upgrade by "
 
161
            "running the command\n"
 
162
            "  bzr upgrade %(basedir)s"),
 
163
        )
 
164
 
110
165
    def __init__(self):
111
166
        self._task_stack = []
 
167
        self.suppressed_warnings = set()
112
168
        self._quiet = False
113
169
 
 
170
    def __enter__(self):
 
171
        """Context manager entry support.
 
172
 
 
173
        Override in a concrete factory class if initialisation before use is
 
174
        needed.
 
175
        """
 
176
        return self # This is bound to the 'as' clause in a with statement.
 
177
 
 
178
    def __exit__(self, exc_type, exc_val, exc_tb):
 
179
        """Context manager exit support.
 
180
 
 
181
        Override in a concrete factory class if more cleanup than a simple
 
182
        self.clear_term() is needed when the UIFactory is finished with.
 
183
        """
 
184
        self.clear_term()
 
185
        return False # propogate exceptions.
 
186
 
114
187
    def be_quiet(self, state):
115
188
        """Tell the UI to be more quiet, or not.
116
189
 
119
192
        """
120
193
        self._quiet = state
121
194
 
 
195
    def confirm_action(self, prompt, confirmation_id, prompt_kwargs):
 
196
        """Seek user confirmation for an action.
 
197
 
 
198
        If the UI is noninteractive, or the user does not want to be asked
 
199
        about this action, True is returned, indicating bzr should just
 
200
        proceed.
 
201
 
 
202
        The confirmation id allows the user to configure certain actions to
 
203
        always be confirmed or always denied, and for UIs to specialize the
 
204
        display of particular confirmations.
 
205
 
 
206
        :param prompt: Suggested text to display to the user.
 
207
        :param prompt_kwargs: A dictionary of arguments that can be
 
208
            string-interpolated into the prompt.
 
209
        :param confirmation_id: Unique string identifier for the confirmation.
 
210
        """
 
211
        return self.get_boolean(prompt % prompt_kwargs)
 
212
 
122
213
    def get_password(self, prompt='', **kwargs):
123
214
        """Prompt the user for a password.
124
215
 
146
237
        version of stdout, but in a GUI it might be appropriate to send it to a 
147
238
        window displaying the text.
148
239
     
149
 
        :param encoding: Unicode encoding for output; default is the 
150
 
            terminal encoding, which may be different from the user encoding.
 
240
        :param encoding: Unicode encoding for output; if not specified 
 
241
            uses the configured 'output_encoding' if any; otherwise the 
 
242
            terminal encoding. 
151
243
            (See get_terminal_encoding.)
152
244
 
153
245
        :param encoding_type: How to handle encoding errors:
155
247
        """
156
248
        # XXX: is the caller supposed to close the resulting object?
157
249
        if encoding is None:
158
 
            encoding = osutils.get_terminal_encoding()
 
250
            from bzrlib import config
 
251
            encoding = config.GlobalConfig().get_user_option(
 
252
                'output_encoding')
 
253
        if encoding is None:
 
254
            encoding = osutils.get_terminal_encoding(trace=True)
159
255
        if encoding_type is None:
160
256
            encoding_type = 'replace'
161
257
        out_stream = self._make_output_stream_explicit(encoding, encoding_type)
183
279
        if not self._task_stack:
184
280
            warnings.warn("%r finished but nothing is active"
185
281
                % (task,))
186
 
        elif task != self._task_stack[-1]:
187
 
            warnings.warn("%r is not the active task %r"
188
 
                % (task, self._task_stack[-1]))
 
282
        if task in self._task_stack:
 
283
            self._task_stack.remove(task)
189
284
        else:
190
 
            del self._task_stack[-1]
 
285
            warnings.warn("%r is not in active stack %r"
 
286
                % (task, self._task_stack))
191
287
        if not self._task_stack:
192
288
            self._progress_all_finished()
193
289
 
210
306
        """
211
307
        pass
212
308
 
 
309
    def format_user_warning(self, warning_id, message_args):
 
310
        try:
 
311
            template = self._user_warning_templates[warning_id]
 
312
        except KeyError:
 
313
            fail = "failed to format warning %r, %r" % (warning_id, message_args)
 
314
            warnings.warn(fail)   # so tests will fail etc
 
315
            return fail
 
316
        try:
 
317
            return template % message_args
 
318
        except ValueError, e:
 
319
            fail = "failed to format warning %r, %r: %s" % (
 
320
                warning_id, message_args, e)
 
321
            warnings.warn(fail)   # so tests will fail etc
 
322
            return fail
 
323
 
213
324
    def get_boolean(self, prompt):
214
325
        """Get a boolean question answered from the user.
215
326
 
237
348
        """
238
349
        return NullProgressView()
239
350
 
240
 
    def recommend_upgrade(self,
241
 
        current_format_name,
242
 
        basedir):
243
 
        # this should perhaps be in the TextUIFactory and the default can do
244
 
        # nothing
245
 
        trace.warning("%s is deprecated "
246
 
            "and a better format is available.\n"
247
 
            "It is recommended that you upgrade by "
248
 
            "running the command\n"
249
 
            "  bzr upgrade %s",
250
 
            current_format_name,
251
 
            basedir)
 
351
    def recommend_upgrade(self, current_format_name, basedir):
 
352
        """Recommend the user upgrade a control directory.
 
353
 
 
354
        :param current_format_name: Description of the current format
 
355
        :param basedir: Location of the control dir
 
356
        """
 
357
        self.show_user_warning('recommend_upgrade',
 
358
            current_format_name=current_format_name, basedir=basedir)
252
359
 
253
360
    def report_transport_activity(self, transport, byte_count, direction):
254
361
        """Called by transports as they do IO.
271
378
        # Default implementation just does nothing
272
379
        pass
273
380
 
 
381
    def show_user_warning(self, warning_id, **message_args):
 
382
        """Show a warning to the user.
 
383
 
 
384
        This is specifically for things that are under the user's control (eg
 
385
        outdated formats), not for internal program warnings like deprecated
 
386
        APIs.
 
387
 
 
388
        This can be overridden by UIFactory subclasses to show it in some 
 
389
        appropriate way; the default UIFactory is noninteractive and does
 
390
        nothing.  format_user_warning maps it to a string, though other
 
391
        presentations can be used for particular UIs.
 
392
 
 
393
        :param warning_id: An identifier like 'cross_format_fetch' used to 
 
394
            check if the message is suppressed and to look up the string.
 
395
        :param message_args: Arguments to be interpolated into the message.
 
396
        """
 
397
        pass
 
398
 
274
399
    def show_error(self, msg):
275
400
        """Show an error message (not an exception) to the user.
276
401
        
288
413
        raise NotImplementedError(self.show_warning)
289
414
 
290
415
    def warn_cross_format_fetch(self, from_format, to_format):
291
 
        """Warn about a potentially slow cross-format transfer"""
292
 
        # See <https://launchpad.net/bugs/456077> asking for a warning here
293
 
        trace.warning("Doing on-the-fly conversion from %s to %s.\n"
294
 
            "This may take some time. Upgrade the repositories to the "
295
 
            "same format for better performance.\n" %
296
 
            (from_format, to_format))
297
 
 
298
 
 
299
 
class SilentUIFactory(UIFactory):
 
416
        """Warn about a potentially slow cross-format transfer.
 
417
        
 
418
        This is deprecated in favor of show_user_warning, but retained for api
 
419
        compatibility in 2.0 and 2.1.
 
420
        """
 
421
        self.show_user_warning('cross_format_fetch', from_format=from_format,
 
422
            to_format=to_format)
 
423
 
 
424
    def warn_experimental_format_fetch(self, inter):
 
425
        """Warn about fetching into experimental repository formats."""
 
426
        if inter.target._format.experimental:
 
427
            trace.warning("Fetching into experimental format %s.\n"
 
428
                "This format may be unreliable or change in the future "
 
429
                "without an upgrade path.\n" % (inter.target._format,))
 
430
 
 
431
 
 
432
class NoninteractiveUIFactory(UIFactory):
 
433
    """Base class for UIs with no user."""
 
434
 
 
435
    def confirm_action(self, prompt, confirmation_id, prompt_kwargs):
 
436
        return True
 
437
 
 
438
    def __repr__(self):
 
439
        return '%s()' % (self.__class__.__name__, )
 
440
 
 
441
 
 
442
class SilentUIFactory(NoninteractiveUIFactory):
300
443
    """A UI Factory which never prints anything.
301
444
 
302
445
    This is the default UI, if another one is never registered by a program
337
480
    def __repr__(self):
338
481
        return "%s(%r)" % (self.__class__.__name__, self.responses)
339
482
 
 
483
    def confirm_action(self, prompt, confirmation_id, args):
 
484
        return self.get_boolean(prompt % args)
 
485
 
340
486
    def get_boolean(self, prompt):
341
487
        return self.responses.pop(0)
342
488