Features requiring application changes¶
Multiline commands¶
Command input may span multiple lines for the
commands whose names are listed in the
parameter app.multiline_commands
. These
commands will be executed only
after the user has entered a terminator.
By default, the command terminator is
;
; replacing or appending to the list
app.terminators
allows different
terminators. A blank line
is always considered a command terminator
(cannot be overridden).
In multiline commands, output redirection characters
like >
and |
are part of the command
arguments unless they appear after the terminator.
Parsed statements¶
cmd2
passes arg
to a do_
method (or
default
) as a Statement, a subclass of
string that includes many attributes of the parsed
input:
- command
- Name of the command called
- args
- The arguments to the command with output redirection or piping to shell commands removed
- command_and_args
- A string of just the command and the arguments, with output redirection or piping to shell commands removed
- argv
- A list of arguments a-la
sys.argv
, including the command asargv[0]
and the subsequent arguments as additional items in the list. Quotes around arguments will be stripped as will any output redirection or piping portions of the command - raw
- Full input exactly as typed.
- terminator
- Character used to end a multiline command
If Statement
does not contain an attribute,
querying for it will return None
.
(Getting arg
as a Statement
is
technically “free”, in that it requires no application
changes from the cmd standard, but there will
be no result unless you change your application
to use any of the additional attributes.)
Environment parameters¶
Your application can define user-settable parameters which your code can
reference. First create a class attribute with the default value. Then
update the settable
dictionary with your setting name and a short
description before you initialize the superclass. Here’s an example, from
examples/environment.py
:
#!/usr/bin/env python
# coding=utf-8
"""
A sample application for cmd2 demonstrating customized environment parameters
"""
import cmd2
class EnvironmentApp(cmd2.Cmd):
""" Example cmd2 application. """
degrees_c = 22
sunny = False
def __init__(self):
self.settable.update({'degrees_c': 'Temperature in Celsius'})
self.settable.update({'sunny': 'Is it sunny outside?'})
super().__init__()
def do_sunbathe(self, arg):
if self.degrees_c < 20:
result = "It's {} C - are you a penguin?".format(self.degrees_c)
elif not self.sunny:
result = 'Too dim.'
else:
result = 'UV is bad for your skin.'
self.poutput(result)
def _onchange_degrees_c(self, old, new):
# if it's over 40C, it's gotta be sunny, right?
if new > 40:
self.sunny = True
if __name__ == '__main__':
c = EnvironmentApp()
c.cmdloop()
If you want to be notified when a setting changes (as we do above), then
define a method _onchange_{setting}()
. This method will be called after
the user changes a setting, and will receive both the old value and the new
value.
(Cmd) set --long | grep sunny
sunny: False # Is it sunny outside?
(Cmd) set --long | grep degrees
degrees_c: 22 # Temperature in Celsius
(Cmd) sunbathe
Too dim.
(Cmd) set degrees_c 41
degrees_c - was: 22
now: 41
(Cmd) set sunny
sunny: True
(Cmd) sunbathe
UV is bad for your skin.
(Cmd) set degrees_c 13
degrees_c - was: 41
now: 13
(Cmd) sunbathe
It's 13 C - are you a penguin?
Commands with flags¶
All do_
methods are responsible for interpreting
the arguments passed to them. However, cmd2
lets
a do_
methods accept Unix-style flags. It uses argparse
to parse the flags, and they work the same way as for
that module.
cmd2
defines a few decorators which change the behavior of
how arguments get parsed for and passed to a do_
method. See the section Argument Processing for more information.
poutput, pfeedback, perror, ppaged¶
Standard cmd
applications produce their output with self.stdout.write('output')
(or with print
,
but print
decreases output flexibility). cmd2
applications can use
self.poutput('output')
, self.pfeedback('message')
, self.perror('errmsg')
, and self.ppaged('text')
instead. These methods have these advantages:
- Handle output redirection to file and/or pipe appropriately
- More concise
.pfeedback()
destination is controlled by Suppressing non-essential output parameter.
- Option to display long output using a pager via
ppaged()
-
Cmd.
poutput
(msg: Any, end: str = '\n', color: str = '') → None¶ Smarter self.stdout.write(); color aware and adds newline of not present.
Also handles BrokenPipeError exceptions for when a commands’s output has been piped to another process and that process terminates before the cmd2 command is finished executing.
Parameters: - msg – message to print to current stdout (anything convertible to a str with ‘{}’.format() is OK)
- end – (optional) string appended after the end of the message if not already present, default a newline
- color – (optional) color escape to output this message with
-
Cmd.
perror
(err: Union[str, Exception], traceback_war: bool = True, err_color: str = '\x1b[91m', war_color: str = '\x1b[93m') → None¶ Print error message to sys.stderr and if debug is true, print an exception Traceback if one exists.
Parameters: - err – an Exception or error message to print out
- traceback_war – (optional) if True, print a message to let user know they can enable debug
- err_color – (optional) color escape to output error with
- war_color – (optional) color escape to output warning with
-
Cmd.
pfeedback
(msg: str) → None¶ For printing nonessential feedback. Can be silenced with quiet. Inclusion in redirected output is controlled by feedback_to_output.
-
Cmd.
ppaged
(msg: str, end: str = '\n', chop: bool = False) → None¶ Print output using a pager if it would go off screen and stdout isn’t currently being redirected.
Never uses a pager inside of a script (Python or text) or when output is being redirected or piped or when stdout or stdin are not a fully functional terminal.
Parameters: - msg – message to print to current stdout (anything convertible to a str with ‘{}’.format() is OK)
- end – string appended after the end of the message if not already present, default a newline
- chop –
- True -> causes lines longer than the screen width to be chopped (truncated) rather than wrapped
- truncated text is still accessible by scrolling with the right & left arrow keys
- chopping is ideal for displaying wide tabular data as is done in utilities like pgcli
- False -> causes lines longer than the screen width to wrap to the next line
- wrapping is ideal when you want to keep users from having to use horizontal scrolling
WARNING: On Windows, the text always wraps regardless of what the chop argument is set to
Colored Output¶
The output methods in the previous section all honor the colors
setting,
which has three possible values:
- Never
- poutput() and pfeedback() strip all ANSI escape sequences which instruct the terminal to colorize output
- Terminal
- (the default value) poutput() and pfeedback() do not strip any ANSI escape sequences when the output is a terminal, but if the output is a pipe or a file the escape sequences are stripped. If you want colorized output you must add ANSI escape sequences, preferably using some python color library like plumbum.colors, colorama, blessings, or termcolor.
- Always
- poutput() and pfeedback() never strip ANSI escape sequences, regardless of the output destination
The previously recommended colorize
method is now deprecated.
Suppressing non-essential output¶
The quiet
setting controls whether self.pfeedback()
actually produces
any output. If quiet
is False
, then the output will be produced. If
quiet
is True
, no output will be produced.
This makes self.pfeedback()
useful for non-essential output like status
messages. Users can control whether they would like to see these messages by changing
the value of the quiet
setting.
select¶
Presents numbered options to user, as bash select
.
app.select
is called from within a method (not by the user directly; it is app.select
, not app.do_select
).
-
Cmd.
select
(opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]], prompt: str = 'Your choice? ') → str¶ Presents a numbered menu to the user. Modeled after the bash shell’s SELECT. Returns the item chosen.
Argument
opts
can be:a single string -> will be split into one-word optionsa list of strings -> will be offered as optionsa list of tuples -> interpreted as (value, text), so that the return value can differ from the text advertised to the user
def do_eat(self, arg):
sauce = self.select('sweet salty', 'Sauce? ')
result = '{food} with {sauce} sauce, yum!'
result = result.format(food=arg, sauce=sauce)
self.stdout.write(result + '\n')
(Cmd) eat wheaties
1. sweet
2. salty
Sauce? 2
wheaties with salty sauce, yum!
Exit code to shell¶
The self.exit_code
attribute of your cmd2
application controls
what exit code is sent to the shell when your application exits from
cmdloop()
.
Asynchronous Feedback¶
cmd2
provides two functions to provide asynchronous feedback to the user without interfering with
the command line. This means the feedback is provided to the user when they are still entering text at
the prompt. To use this functionality, the application must be running in a terminal that supports
VT100 control characters and readline. Linux, Mac, and Windows 10 and greater all support these.
- async_alert()
- Used to display an important message to the user while they are at the prompt in between commands. To the user it appears as if an alert message is printed above the prompt and their current input text and cursor location is left alone.
- async_update_prompt()
- Updates the prompt while the user is still typing at it. This is good for alerting the user to system changes dynamically in between commands. For instance you could alter the color of the prompt to indicate a system status or increase a counter to report an event.
cmd2
also provides a function to change the title of the terminal window. This feature requires the
application be running in a terminal that supports VT100 control characters. Linux, Mac, and Windows 10 and
greater all support these.
- set_window_title()
- Sets the terminal window title
The easiest way to understand these functions is to see the AsyncPrinting example for a demonstration.