~malept/ubuntu/lucid/python2.6/dev-dependency-fix

« back to all changes in this revision

Viewing changes to Lib/cmd.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-02-13 12:51:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090213125100-uufgcb9yeqzujpqw
Tags: upstream-2.6.1
ImportĀ upstreamĀ versionĀ 2.6.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""A generic class to build line-oriented command interpreters.
 
2
 
 
3
Interpreters constructed with this class obey the following conventions:
 
4
 
 
5
1. End of file on input is processed as the command 'EOF'.
 
6
2. A command is parsed out of each line by collecting the prefix composed
 
7
   of characters in the identchars member.
 
8
3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method
 
9
   is passed a single argument consisting of the remainder of the line.
 
10
4. Typing an empty line repeats the last command.  (Actually, it calls the
 
11
   method `emptyline', which may be overridden in a subclass.)
 
12
5. There is a predefined `help' method.  Given an argument `topic', it
 
13
   calls the command `help_topic'.  With no arguments, it lists all topics
 
14
   with defined help_ functions, broken into up to three topics; documented
 
15
   commands, miscellaneous help topics, and undocumented commands.
 
16
6. The command '?' is a synonym for `help'.  The command '!' is a synonym
 
17
   for `shell', if a do_shell method exists.
 
18
7. If completion is enabled, completing commands will be done automatically,
 
19
   and completing of commands args is done by calling complete_foo() with
 
20
   arguments text, line, begidx, endidx.  text is string we are matching
 
21
   against, all returned matches must begin with it.  line is the current
 
22
   input line (lstripped), begidx and endidx are the beginning and end
 
23
   indexes of the text being matched, which could be used to provide
 
24
   different completion depending upon which position the argument is in.
 
25
 
 
26
The `default' method may be overridden to intercept commands for which there
 
27
is no do_ method.
 
28
 
 
29
The `completedefault' method may be overridden to intercept completions for
 
30
commands that have no complete_ method.
 
31
 
 
32
The data member `self.ruler' sets the character used to draw separator lines
 
33
in the help messages.  If empty, no ruler line is drawn.  It defaults to "=".
 
34
 
 
35
If the value of `self.intro' is nonempty when the cmdloop method is called,
 
36
it is printed out on interpreter startup.  This value may be overridden
 
37
via an optional argument to the cmdloop() method.
 
38
 
 
39
The data members `self.doc_header', `self.misc_header', and
 
40
`self.undoc_header' set the headers used for the help function's
 
41
listings of documented functions, miscellaneous topics, and undocumented
 
42
functions respectively.
 
43
 
 
44
These interpreters use raw_input; thus, if the readline module is loaded,
 
45
they automatically support Emacs-like command history and editing features.
 
46
"""
 
47
 
 
48
import string
 
49
 
 
50
__all__ = ["Cmd"]
 
51
 
 
52
PROMPT = '(Cmd) '
 
53
IDENTCHARS = string.ascii_letters + string.digits + '_'
 
54
 
 
55
class Cmd:
 
56
    """A simple framework for writing line-oriented command interpreters.
 
57
 
 
58
    These are often useful for test harnesses, administrative tools, and
 
59
    prototypes that will later be wrapped in a more sophisticated interface.
 
60
 
 
61
    A Cmd instance or subclass instance is a line-oriented interpreter
 
62
    framework.  There is no good reason to instantiate Cmd itself; rather,
 
63
    it's useful as a superclass of an interpreter class you define yourself
 
64
    in order to inherit Cmd's methods and encapsulate action methods.
 
65
 
 
66
    """
 
67
    prompt = PROMPT
 
68
    identchars = IDENTCHARS
 
69
    ruler = '='
 
70
    lastcmd = ''
 
71
    intro = None
 
72
    doc_leader = ""
 
73
    doc_header = "Documented commands (type help <topic>):"
 
74
    misc_header = "Miscellaneous help topics:"
 
75
    undoc_header = "Undocumented commands:"
 
76
    nohelp = "*** No help on %s"
 
77
    use_rawinput = 1
 
78
 
 
79
    def __init__(self, completekey='tab', stdin=None, stdout=None):
 
80
        """Instantiate a line-oriented interpreter framework.
 
81
 
 
82
        The optional argument 'completekey' is the readline name of a
 
83
        completion key; it defaults to the Tab key. If completekey is
 
84
        not None and the readline module is available, command completion
 
85
        is done automatically. The optional arguments stdin and stdout
 
86
        specify alternate input and output file objects; if not specified,
 
87
        sys.stdin and sys.stdout are used.
 
88
 
 
89
        """
 
90
        import sys
 
91
        if stdin is not None:
 
92
            self.stdin = stdin
 
93
        else:
 
94
            self.stdin = sys.stdin
 
95
        if stdout is not None:
 
96
            self.stdout = stdout
 
97
        else:
 
98
            self.stdout = sys.stdout
 
99
        self.cmdqueue = []
 
100
        self.completekey = completekey
 
101
 
 
102
    def cmdloop(self, intro=None):
 
103
        """Repeatedly issue a prompt, accept input, parse an initial prefix
 
104
        off the received input, and dispatch to action methods, passing them
 
105
        the remainder of the line as argument.
 
106
 
 
107
        """
 
108
 
 
109
        self.preloop()
 
110
        if self.use_rawinput and self.completekey:
 
111
            try:
 
112
                import readline
 
113
                self.old_completer = readline.get_completer()
 
114
                readline.set_completer(self.complete)
 
115
                readline.parse_and_bind(self.completekey+": complete")
 
116
            except ImportError:
 
117
                pass
 
118
        try:
 
119
            if intro is not None:
 
120
                self.intro = intro
 
121
            if self.intro:
 
122
                self.stdout.write(str(self.intro)+"\n")
 
123
            stop = None
 
124
            while not stop:
 
125
                if self.cmdqueue:
 
126
                    line = self.cmdqueue.pop(0)
 
127
                else:
 
128
                    if self.use_rawinput:
 
129
                        try:
 
130
                            line = raw_input(self.prompt)
 
131
                        except EOFError:
 
132
                            line = 'EOF'
 
133
                    else:
 
134
                        self.stdout.write(self.prompt)
 
135
                        self.stdout.flush()
 
136
                        line = self.stdin.readline()
 
137
                        if not len(line):
 
138
                            line = 'EOF'
 
139
                        else:
 
140
                            line = line[:-1] # chop \n
 
141
                line = self.precmd(line)
 
142
                stop = self.onecmd(line)
 
143
                stop = self.postcmd(stop, line)
 
144
            self.postloop()
 
145
        finally:
 
146
            if self.use_rawinput and self.completekey:
 
147
                try:
 
148
                    import readline
 
149
                    readline.set_completer(self.old_completer)
 
150
                except ImportError:
 
151
                    pass
 
152
 
 
153
 
 
154
    def precmd(self, line):
 
155
        """Hook method executed just before the command line is
 
156
        interpreted, but after the input prompt is generated and issued.
 
157
 
 
158
        """
 
159
        return line
 
160
 
 
161
    def postcmd(self, stop, line):
 
162
        """Hook method executed just after a command dispatch is finished."""
 
163
        return stop
 
164
 
 
165
    def preloop(self):
 
166
        """Hook method executed once when the cmdloop() method is called."""
 
167
        pass
 
168
 
 
169
    def postloop(self):
 
170
        """Hook method executed once when the cmdloop() method is about to
 
171
        return.
 
172
 
 
173
        """
 
174
        pass
 
175
 
 
176
    def parseline(self, line):
 
177
        """Parse the line into a command name and a string containing
 
178
        the arguments.  Returns a tuple containing (command, args, line).
 
179
        'command' and 'args' may be None if the line couldn't be parsed.
 
180
        """
 
181
        line = line.strip()
 
182
        if not line:
 
183
            return None, None, line
 
184
        elif line[0] == '?':
 
185
            line = 'help ' + line[1:]
 
186
        elif line[0] == '!':
 
187
            if hasattr(self, 'do_shell'):
 
188
                line = 'shell ' + line[1:]
 
189
            else:
 
190
                return None, None, line
 
191
        i, n = 0, len(line)
 
192
        while i < n and line[i] in self.identchars: i = i+1
 
193
        cmd, arg = line[:i], line[i:].strip()
 
194
        return cmd, arg, line
 
195
 
 
196
    def onecmd(self, line):
 
197
        """Interpret the argument as though it had been typed in response
 
198
        to the prompt.
 
199
 
 
200
        This may be overridden, but should not normally need to be;
 
201
        see the precmd() and postcmd() methods for useful execution hooks.
 
202
        The return value is a flag indicating whether interpretation of
 
203
        commands by the interpreter should stop.
 
204
 
 
205
        """
 
206
        cmd, arg, line = self.parseline(line)
 
207
        if not line:
 
208
            return self.emptyline()
 
209
        if cmd is None:
 
210
            return self.default(line)
 
211
        self.lastcmd = line
 
212
        if cmd == '':
 
213
            return self.default(line)
 
214
        else:
 
215
            try:
 
216
                func = getattr(self, 'do_' + cmd)
 
217
            except AttributeError:
 
218
                return self.default(line)
 
219
            return func(arg)
 
220
 
 
221
    def emptyline(self):
 
222
        """Called when an empty line is entered in response to the prompt.
 
223
 
 
224
        If this method is not overridden, it repeats the last nonempty
 
225
        command entered.
 
226
 
 
227
        """
 
228
        if self.lastcmd:
 
229
            return self.onecmd(self.lastcmd)
 
230
 
 
231
    def default(self, line):
 
232
        """Called on an input line when the command prefix is not recognized.
 
233
 
 
234
        If this method is not overridden, it prints an error message and
 
235
        returns.
 
236
 
 
237
        """
 
238
        self.stdout.write('*** Unknown syntax: %s\n'%line)
 
239
 
 
240
    def completedefault(self, *ignored):
 
241
        """Method called to complete an input line when no command-specific
 
242
        complete_*() method is available.
 
243
 
 
244
        By default, it returns an empty list.
 
245
 
 
246
        """
 
247
        return []
 
248
 
 
249
    def completenames(self, text, *ignored):
 
250
        dotext = 'do_'+text
 
251
        return [a[3:] for a in self.get_names() if a.startswith(dotext)]
 
252
 
 
253
    def complete(self, text, state):
 
254
        """Return the next possible completion for 'text'.
 
255
 
 
256
        If a command has not been entered, then complete against command list.
 
257
        Otherwise try to call complete_<command> to get list of completions.
 
258
        """
 
259
        if state == 0:
 
260
            import readline
 
261
            origline = readline.get_line_buffer()
 
262
            line = origline.lstrip()
 
263
            stripped = len(origline) - len(line)
 
264
            begidx = readline.get_begidx() - stripped
 
265
            endidx = readline.get_endidx() - stripped
 
266
            if begidx>0:
 
267
                cmd, args, foo = self.parseline(line)
 
268
                if cmd == '':
 
269
                    compfunc = self.completedefault
 
270
                else:
 
271
                    try:
 
272
                        compfunc = getattr(self, 'complete_' + cmd)
 
273
                    except AttributeError:
 
274
                        compfunc = self.completedefault
 
275
            else:
 
276
                compfunc = self.completenames
 
277
            self.completion_matches = compfunc(text, line, begidx, endidx)
 
278
        try:
 
279
            return self.completion_matches[state]
 
280
        except IndexError:
 
281
            return None
 
282
 
 
283
    def get_names(self):
 
284
        # Inheritance says we have to look in class and
 
285
        # base classes; order is not important.
 
286
        names = []
 
287
        classes = [self.__class__]
 
288
        while classes:
 
289
            aclass = classes.pop(0)
 
290
            if aclass.__bases__:
 
291
                classes = classes + list(aclass.__bases__)
 
292
            names = names + dir(aclass)
 
293
        return names
 
294
 
 
295
    def complete_help(self, *args):
 
296
        return self.completenames(*args)
 
297
 
 
298
    def do_help(self, arg):
 
299
        if arg:
 
300
            # XXX check arg syntax
 
301
            try:
 
302
                func = getattr(self, 'help_' + arg)
 
303
            except AttributeError:
 
304
                try:
 
305
                    doc=getattr(self, 'do_' + arg).__doc__
 
306
                    if doc:
 
307
                        self.stdout.write("%s\n"%str(doc))
 
308
                        return
 
309
                except AttributeError:
 
310
                    pass
 
311
                self.stdout.write("%s\n"%str(self.nohelp % (arg,)))
 
312
                return
 
313
            func()
 
314
        else:
 
315
            names = self.get_names()
 
316
            cmds_doc = []
 
317
            cmds_undoc = []
 
318
            help = {}
 
319
            for name in names:
 
320
                if name[:5] == 'help_':
 
321
                    help[name[5:]]=1
 
322
            names.sort()
 
323
            # There can be duplicates if routines overridden
 
324
            prevname = ''
 
325
            for name in names:
 
326
                if name[:3] == 'do_':
 
327
                    if name == prevname:
 
328
                        continue
 
329
                    prevname = name
 
330
                    cmd=name[3:]
 
331
                    if cmd in help:
 
332
                        cmds_doc.append(cmd)
 
333
                        del help[cmd]
 
334
                    elif getattr(self, name).__doc__:
 
335
                        cmds_doc.append(cmd)
 
336
                    else:
 
337
                        cmds_undoc.append(cmd)
 
338
            self.stdout.write("%s\n"%str(self.doc_leader))
 
339
            self.print_topics(self.doc_header,   cmds_doc,   15,80)
 
340
            self.print_topics(self.misc_header,  help.keys(),15,80)
 
341
            self.print_topics(self.undoc_header, cmds_undoc, 15,80)
 
342
 
 
343
    def print_topics(self, header, cmds, cmdlen, maxcol):
 
344
        if cmds:
 
345
            self.stdout.write("%s\n"%str(header))
 
346
            if self.ruler:
 
347
                self.stdout.write("%s\n"%str(self.ruler * len(header)))
 
348
            self.columnize(cmds, maxcol-1)
 
349
            self.stdout.write("\n")
 
350
 
 
351
    def columnize(self, list, displaywidth=80):
 
352
        """Display a list of strings as a compact set of columns.
 
353
 
 
354
        Each column is only as wide as necessary.
 
355
        Columns are separated by two spaces (one was not legible enough).
 
356
        """
 
357
        if not list:
 
358
            self.stdout.write("<empty>\n")
 
359
            return
 
360
        nonstrings = [i for i in range(len(list))
 
361
                        if not isinstance(list[i], str)]
 
362
        if nonstrings:
 
363
            raise TypeError, ("list[i] not a string for i in %s" %
 
364
                              ", ".join(map(str, nonstrings)))
 
365
        size = len(list)
 
366
        if size == 1:
 
367
            self.stdout.write('%s\n'%str(list[0]))
 
368
            return
 
369
        # Try every row count from 1 upwards
 
370
        for nrows in range(1, len(list)):
 
371
            ncols = (size+nrows-1) // nrows
 
372
            colwidths = []
 
373
            totwidth = -2
 
374
            for col in range(ncols):
 
375
                colwidth = 0
 
376
                for row in range(nrows):
 
377
                    i = row + nrows*col
 
378
                    if i >= size:
 
379
                        break
 
380
                    x = list[i]
 
381
                    colwidth = max(colwidth, len(x))
 
382
                colwidths.append(colwidth)
 
383
                totwidth += colwidth + 2
 
384
                if totwidth > displaywidth:
 
385
                    break
 
386
            if totwidth <= displaywidth:
 
387
                break
 
388
        else:
 
389
            nrows = len(list)
 
390
            ncols = 1
 
391
            colwidths = [0]
 
392
        for row in range(nrows):
 
393
            texts = []
 
394
            for col in range(ncols):
 
395
                i = row + nrows*col
 
396
                if i >= size:
 
397
                    x = ""
 
398
                else:
 
399
                    x = list[i]
 
400
                texts.append(x)
 
401
            while texts and not texts[-1]:
 
402
                del texts[-1]
 
403
            for col in range(len(texts)):
 
404
                texts[col] = texts[col].ljust(colwidths[col])
 
405
            self.stdout.write("%s\n"%str("  ".join(texts)))