ยปCore Development>Code coverage>Lib/cmd.py

Python code coverage for Lib/cmd.py

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