~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/ui/__init__.py

  • Committer: Andrew Bennetts
  • Date: 2007-07-17 06:57:01 UTC
  • mto: (2535.3.19 repo-refactor)
  • mto: This revision was merged to the branch mainline in revision 2668.
  • Revision ID: andrew.bennetts@canonical.com-20070717065701-uq95aym0vtp7xykb
Log errors from the smart server in the trace file, to make debugging test failures (and live failures!) easier.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
16
18
 
17
19
"""UI abstraction.
18
20
 
26
28
displays no output.
27
29
"""
28
30
 
29
 
import os
30
31
import sys
31
 
import warnings
32
32
 
33
33
from bzrlib.lazy_import import lazy_import
34
34
lazy_import(globals(), """
35
35
import getpass
36
36
 
37
37
from bzrlib import (
38
 
    errors,
39
38
    osutils,
40
39
    progress,
41
40
    trace,
42
41
    )
43
42
""")
44
43
 
 
44
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
 
45
 
45
46
 
46
47
class UIFactory(object):
47
48
    """UI abstraction.
51
52
    """
52
53
 
53
54
    def __init__(self):
54
 
        self._task_stack = []
 
55
        super(UIFactory, self).__init__()
 
56
        self._progress_bar_stack = None
 
57
 
 
58
    @deprecated_method(zero_eight)
 
59
    def progress_bar(self):
 
60
        """See UIFactory.nested_progress_bar()."""
 
61
        raise NotImplementedError(self.progress_bar)
55
62
 
56
63
    def get_password(self, prompt='', **kwargs):
57
64
        """Prompt the user for a password.
74
81
        When the bar has been finished with, it should be released by calling
75
82
        bar.finished().
76
83
        """
77
 
        if self._task_stack:
78
 
            t = progress.ProgressTask(self._task_stack[-1], self)
79
 
        else:
80
 
            t = progress.ProgressTask(None, self)
81
 
        self._task_stack.append(t)
82
 
        return t
83
 
 
84
 
    def _progress_finished(self, task):
85
 
        """Called by the ProgressTask when it finishes"""
86
 
        if not self._task_stack:
87
 
            warnings.warn("%r finished but nothing is active"
88
 
                % (task,))
89
 
        elif task != self._task_stack[-1]:
90
 
            warnings.warn("%r is not the active task %r"
91
 
                % (task, self._task_stack[-1]))
92
 
        else:
93
 
            del self._task_stack[-1]
94
 
        if not self._task_stack:
95
 
            self._progress_all_finished()
96
 
 
97
 
    def _progress_all_finished(self):
98
 
        """Called when the top-level progress task finished"""
99
 
        pass
100
 
 
101
 
    def _progress_updated(self, task):
102
 
        """Called by the ProgressTask when it changes.
103
 
 
104
 
        Should be specialized to draw the progress.
105
 
        """
106
 
        pass
 
84
        raise NotImplementedError(self.nested_progress_bar)
107
85
 
108
86
    def clear_term(self):
109
87
        """Prepare the terminal for output.
110
88
 
111
89
        This will, for example, clear text progress bars, and leave the
112
 
        cursor at the leftmost position.
113
 
        """
114
 
        pass
 
90
        cursor at the leftmost position."""
 
91
        raise NotImplementedError(self.clear_term)
115
92
 
116
93
    def get_boolean(self, prompt):
117
 
        """Get a boolean question answered from the user.
 
94
        """Get a boolean question answered from the user. 
118
95
 
119
96
        :param prompt: a message to prompt the user with. Should be a single
120
97
        line without terminating \n.
135
112
            current_format_name,
136
113
            basedir)
137
114
 
138
 
    def report_transport_activity(self, transport, byte_count, direction):
139
 
        """Called by transports as they do IO.
140
 
 
141
 
        This may update a progress bar, spinner, or similar display.
142
 
        By default it does nothing.
143
 
        """
144
 
        pass
145
 
 
146
 
 
147
115
 
148
116
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
 
 
153
 
    def __init__(self, stdin=None, stdout=None, stderr=None):
154
 
        UIFactory.__init__(self)
155
 
        self.stdin = stdin or sys.stdin
156
 
        self.stdout = stdout or sys.stdout
157
 
        self.stderr = stderr or sys.stderr
 
117
    """Common behaviour for command line UI factories."""
 
118
 
 
119
    def __init__(self):
 
120
        super(CLIUIFactory, self).__init__()
 
121
        self.stdin = sys.stdin
158
122
 
159
123
    def get_boolean(self, prompt):
 
124
        self.clear_term()
160
125
        # FIXME: make a regexp and handle case variations as well.
161
126
        while True:
162
127
            self.prompt(prompt + "? [y/n]: ")
166
131
            if line in ('n\n', 'no\n'):
167
132
                return False
168
133
 
169
 
    def get_non_echoed_password(self):
170
 
        isatty = getattr(self.stdin, 'isatty', None)
171
 
        if isatty is not None and isatty():
172
 
            # getpass() ensure the password is not echoed and other
173
 
            # cross-platform niceties
174
 
            password = getpass.getpass('')
175
 
        else:
176
 
            # echo doesn't make sense without a terminal
177
 
            password = self.stdin.readline()
178
 
            if not password:
179
 
                password = None
180
 
            elif password[-1] == '\n':
181
 
                password = password[:-1]
182
 
        return password
 
134
    def get_non_echoed_password(self, prompt):
 
135
        encoding = osutils.get_terminal_encoding()
 
136
        return getpass.getpass(prompt.encode(encoding, 'replace'))
183
137
 
184
138
    def get_password(self, prompt='', **kwargs):
185
139
        """Prompt the user for a password.
188
142
        :param kwargs: Arguments which will be expanded into the prompt.
189
143
                       This lets front ends display different things if
190
144
                       they so choose.
191
 
        :return: The password string, return None if the user
 
145
        :return: The password string, return None if the user 
192
146
                 canceled the request.
193
147
        """
194
148
        prompt += ': '
195
 
        self.prompt(prompt, **kwargs)
 
149
        prompt = (prompt % kwargs)
196
150
        # There's currently no way to say 'i decline to enter a password'
197
151
        # as opposed to 'my password is empty' -- does it matter?
198
 
        return self.get_non_echoed_password()
199
 
 
200
 
    def get_username(self, prompt, **kwargs):
201
 
        """Prompt the user for a username.
202
 
 
203
 
        :param prompt: The prompt to present the user
204
 
        :param kwargs: Arguments which will be expanded into the prompt.
205
 
                       This lets front ends display different things if
206
 
                       they so choose.
207
 
        :return: The username string, return None if the user
208
 
                 canceled the request.
209
 
        """
210
 
        prompt += ': '
211
 
        self.prompt(prompt, **kwargs)
212
 
        username = self.stdin.readline()
213
 
        if not username:
214
 
            username = None
215
 
        elif username[-1] == '\n':
216
 
            username = username[:-1]
217
 
        return username
218
 
 
219
 
    def prompt(self, prompt, **kwargs):
 
152
        return self.get_non_echoed_password(prompt)
 
153
 
 
154
    def prompt(self, prompt):
220
155
        """Emit prompt on the CLI."""
221
 
        prompt = prompt % kwargs
222
 
        prompt = prompt.encode(osutils.get_terminal_encoding(), 'replace')
223
 
        self.clear_term()
224
 
        self.stdout.write(prompt)
225
 
 
226
 
    def note(self, msg):
227
 
        """Write an already-formatted message."""
228
 
        self.stdout.write(msg + '\n')
229
156
 
230
157
 
231
158
class SilentUIFactory(CLIUIFactory):
234
161
    This is the default UI, if another one is never registered.
235
162
    """
236
163
 
237
 
    def __init__(self):
238
 
        CLIUIFactory.__init__(self)
 
164
    @deprecated_method(zero_eight)
 
165
    def progress_bar(self):
 
166
        """See UIFactory.nested_progress_bar()."""
 
167
        return progress.DummyProgress()
239
168
 
240
169
    def get_password(self, prompt='', **kwargs):
241
170
        return None
242
171
 
243
 
    def get_username(self, prompt='', **kwargs):
244
 
        return None
 
172
    def nested_progress_bar(self):
 
173
        if self._progress_bar_stack is None:
 
174
            self._progress_bar_stack = progress.ProgressBarStack(
 
175
                klass=progress.DummyProgress)
 
176
        return self._progress_bar_stack.get_nested()
245
177
 
246
 
    def prompt(self, prompt, **kwargs):
 
178
    def clear_term(self):
247
179
        pass
248
180
 
249
 
    def note(self, msg):
 
181
    def recommend_upgrade(self, *args):
250
182
        pass
251
183
 
252
184
 
257
189
 
258
190
 
259
191
ui_factory = SilentUIFactory()
260
 
"""IMPORTANT: never import this symbol directly. ONLY ever access it as
 
192
"""IMPORTANT: never import this symbol directly. ONLY ever access it as 
261
193
ui.ui_factory."""
262
 
 
263
 
 
264
 
def make_ui_for_terminal(stdin, stdout, stderr):
265
 
    """Construct and return a suitable UIFactory for a text mode program.
266
 
 
267
 
    If stdout is a smart terminal, this gets a smart UIFactory with
268
 
    progress indicators, etc.  If it's a dumb terminal, just plain text output.
269
 
    """
270
 
    cls = None
271
 
    isatty = getattr(stdin, 'isatty', None)
272
 
    if isatty is None:
273
 
        cls = CLIUIFactory
274
 
    elif not isatty():
275
 
        cls = CLIUIFactory
276
 
    elif os.environ.get('TERM') in ('dumb', ''):
277
 
        # e.g. emacs compile window
278
 
        cls = CLIUIFactory
279
 
    # User may know better, otherwise default to TextUIFactory
280
 
    if (   os.environ.get('BZR_USE_TEXT_UI', None) is not None
281
 
        or cls is None):
282
 
        from bzrlib.ui.text import TextUIFactory
283
 
        cls = TextUIFactory
284
 
    return cls(stdin=stdin, stdout=stdout, stderr=stderr)