Skip to content

Commit 27a0adb

Browse files
authored
Merge pull request #780 from python-cmd2/doc_additions
Doc additions
2 parents 1176c0c + b45fec9 commit 27a0adb

File tree

6 files changed

+277
-35
lines changed

6 files changed

+277
-35
lines changed

docs/examples/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@ Examples
55
:maxdepth: 1
66

77
first_app
8-
removing_builtin_commands
98
alternate_event_loops

docs/examples/removing_builtin_commands.rst

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/features/commands.rst

Lines changed: 173 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,196 @@ Commands
33

44
.. _cmd: https://docs.python.org/3/library/cmd.html
55

6-
How to create a command with a ``do_command`` method,
6+
``cmd2`` is designed to make it easy for you to create new commands. These
7+
commmands form the backbone of your application. If you started writing your
8+
application using cmd_, all the commands you have built will work when you move
9+
to ``cmd2``. However, there are many more capabilities available in ``cmd2``
10+
which you can take advantage of to add more robust features to your commands,
11+
and which makes your commands easier to write. Before we get to all the good
12+
stuff, let's briefly discuss how to create a new command in your application.
713

8-
Parsed statements
9-
-----------------
1014

11-
``cmd2`` passes ``arg`` to a ``do_`` method (or ``default``) as a Statement, a
12-
subclass of string that includes many attributes of the parsed input:
15+
Basic Commands
16+
--------------
17+
18+
The simplest ``cmd2`` application looks like this::
19+
20+
#!/usr/bin/env python
21+
"""A simple cmd2 application."""
22+
import cmd2
23+
24+
25+
class App(cmd2.Cmd):
26+
"""A simple cmd2 application."""
27+
28+
29+
if __name__ == '__main__':
30+
import sys
31+
c = App()
32+
sys.exit(c.cmdloop())
33+
34+
This application subclasses ``cmd2.Cmd`` but has no code of it's own, so all
35+
functionality (and there's quite a bit) is inherited. Lets create a simple
36+
command in this application called ``echo`` which outputs any arguments given
37+
to it. Add this method to the class::
38+
39+
def do_echo(self, line):
40+
self.poutput(line)
41+
42+
When you type input into the ``cmd2`` prompt, the first space delimited word is
43+
treated as the command name. ``cmd2`` looks for a method called
44+
``do_commandname``. If it exists, it calls the method, passing the rest of the
45+
user input as the first argument. If it doesn't exist ``cmd2`` prints an error
46+
message. As a result of this behavior, the only thing you have to do to create
47+
a new command is to define a new method in the class with the appropriate name.
48+
This is exactly how you would create a command using the cmd_ module which is
49+
part of the python standard library.
50+
51+
.. note::
52+
53+
See :ref:`features/generating_output:Generating Output` if you are
54+
unfamiliar with the ``poutput()`` method.
55+
56+
57+
Statements
58+
----------
59+
60+
A command is passed one argument: a string which contains all the rest of the
61+
user input. However, in ``cmd2`` this string is actually a ``Statement``
62+
object, which is a subclass of ``str`` to retain backwards compatibility.
63+
64+
``cmd2`` has a much more sophsticated parsing engine than what's included in
65+
the cmd_ module. This parsing handles:
66+
67+
- quoted arguments
68+
- output redirection and piping
69+
- multi-line commands
70+
- shortcut, macro, and alias expansion
71+
72+
In addition to parsing all of these elements from the user input, ``cmd2`` also
73+
has code to make all of these items work; it's almost transparent to you and to
74+
the commands you write in your own application. However, by passing your
75+
command the ``Statement`` object instead of just a plain string, you can get
76+
visibility into what ``cmd2`` has done with the user input before your command
77+
got it. You can also avoid writing a bunch of parsing code, because ``cmd2``
78+
gives you access to what it has already parsed.
79+
80+
A ``Statement`` object is a subclass of ``str`` that contains the following
81+
attributes:
1382

1483
command
15-
Name of the command called
84+
Name of the command called. You already know this because of the method
85+
``cmd2`` called, but it can sometimes be nice to have it in a string, i.e.
86+
if you want your error messages to contain the command name.
1687

1788
args
18-
The arguments to the command with output redirection
19-
or piping to shell commands removed
89+
A string containing the arguments to the command with output redirection or
90+
piping to shell commands removed. It turns out that the "string" value of
91+
the ``Statement`` object has all the output redirection and piping clauses
92+
removed as well. Quotes remain in the string.
2093

2194
command_and_args
22-
A string of just the command and the arguments, with
23-
output redirection or piping to shell commands removed
95+
A string of just the command and the arguments, with output redirection or
96+
piping to shell commands removed.
2497

2598
argv
26-
A list of arguments a-la ``sys.argv``, including
27-
the command as ``argv[0]`` and the subsequent
28-
arguments as additional items in the list.
29-
Quotes around arguments will be stripped as will
30-
any output redirection or piping portions of the command
99+
A list of arguments a-la ``sys.argv``, including the command as ``argv[0]``
100+
and the subsequent arguments as additional items in the list. Quotes around
101+
arguments will be stripped as will any output redirection or piping
102+
portions of the command.
31103

32104
raw
33-
Full input exactly as typed.
105+
Full input exactly as typed by the user.
34106

35107
terminator
36-
Character used to end a multiline command
108+
Character used to end a multiline command. You can configure multiple
109+
termination characters, and this attribute will tell you which one the user
110+
typed.
111+
112+
For many simple commands, like the ``echo`` command above, you can ignore the
113+
``Statement`` object and all of it's attributes and just use the passed value
114+
as a string. You might choose to use the ``argv`` attribute to do more
115+
sophisticated argument processing. Before you go too far down that path, you
116+
should check out the :ref:`features/argument_processing:Argument Processing`
117+
functionality included with ``cmd2``.
118+
119+
120+
Return Values
121+
-------------
122+
123+
Most commands should return nothing (either by omitting a ``return`` statement,
124+
or by ``return None``. This indicates that your command is finished (with or
125+
without errors), and that ``cmd2`` should prompt the user for more input.
126+
127+
If you return ``True`` from a command method, that indicates to ``cmd2`` that
128+
it should stop prompting for user input and cleanly exit. ``cmd2`` already
129+
includes a ``quit`` command, but if you wanted to make another one called
130+
``finis`` you could::
131+
132+
def do_finis(self, line):
133+
"""Exit the application"""
134+
return True
135+
136+
137+
Exit Codes
138+
----------
139+
140+
``cmd2`` has basic infrastructure to support sh/ksh/csh/bash type exit codes.
141+
The ``cmd2.Cmd`` object sets an ``exit_code`` attribute to zero when it is
142+
instantiated. The value of this attribute is returned from the ``cmdloop()``
143+
call. Therefore, if you don't do anything with this attribute in your code,
144+
``cmdloop()`` will (almost) always return zero. There are a few built-in
145+
``cmd2`` commands which set ``exit_code`` to ``-1`` if an error occurs.
146+
147+
You can use this capability to easily return your own values to the operating
148+
system shell::
149+
150+
#!/usr/bin/env python
151+
"""A simple cmd2 application."""
152+
import cmd2
153+
154+
155+
class App(cmd2.Cmd):
156+
"""A simple cmd2 application."""
157+
158+
def do_bail(self, line):
159+
"""Exit the application""
160+
self.perror("fatal error, exiting")
161+
self.exit_code = 2
162+
return true
163+
164+
if __name__ == '__main__':
165+
import sys
166+
c = App()
167+
sys.exit(c.cmdloop())
168+
169+
If the app was run from the `bash` operating system shell, then you would see
170+
the following interaction::
171+
172+
(Cmd) bail
173+
fatal error, exiting
174+
$ echo $?
175+
2
176+
37177

178+
Exception Handling
179+
------------------
38180

181+
You may choose to catch and handle any exceptions which occur in
182+
a command method. If the command method raises an exception, ``cmd2`` will
183+
catch it and display it for you. The `debug` :ref:`setting
184+
<features/settings:Settings>` controls how the exception is displayed. If
185+
`debug` is `false`, which is the default, ``cmd2`` will display the exception
186+
name and message. If `debug` is `true`, ``cmd2`` will display a traceback, and
187+
then display the exception name and message.
39188

40-
If ``Statement`` does not contain an attribute, querying for it will return
41-
``None``.
42189

43-
(Getting ``arg`` as a ``Statement`` is technically "free", in that it requires
44-
no application changes from the cmd_ standard, but there will be no result
45-
unless you change your application to *use* any of the additional attributes.)
190+
Disabling or Hiding Commands
191+
----------------------------
46192

193+
See :ref:`features/disable_commands:Disabling Commands` for details of how
194+
to:
47195

196+
- remove commands included in ``cmd2``
197+
- hide commands from the help menu
198+
- disable and re-enable commands at runtime

docs/features/disable_commands.rst

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,103 @@
11
Disabling Commands
22
==================
33

4-
How to disable and re-enable commands, by individual command and by category
4+
``cmd2`` allows a developer to:
5+
6+
- remove commands included in ``cmd2``
7+
- prevent commands from appearing in the help menu (hide commands)
8+
- disable and re-enable commands at runtime
9+
10+
11+
Remove A Command
12+
----------------
13+
14+
When a command has been removed, the command method has been deleted from the
15+
object. The command doesn't show up in help, and it can't be executed. This
16+
approach is appropriate if you never want a built-in command to be part of your
17+
application. Delete the command method in your initialization code::
18+
19+
class RemoveBuiltinCommand(cmd2.Cmd):
20+
"""An app which removes a built-in command from cmd2"""
21+
22+
def __init__(self):
23+
super().__init__()
24+
# To remove built-in commands entirely, delete
25+
# the "do_*" function from the cmd2.Cmd class
26+
del cmd2.Cmd.do_edit
27+
28+
29+
Hide A Command
30+
--------------
31+
32+
When a command is hidden, it won't show up in the help menu, but if
33+
the user knows it's there and types the command, it will be executed.
34+
You hide a command by adding it to the ``hidden_commands`` list::
35+
36+
class HiddenCommands(cmd2.Cmd):
37+
""An app which demonstrates how to hide a command"""
38+
def __init__(self):
39+
super().__init__()
40+
self.hidden_commands.append('py')
41+
42+
As shown above, you would typically do this as part of initializing your
43+
application. If you decide you want to unhide a command later in the execution
44+
of your application, you can by doing::
45+
46+
self.hidden_commands = [cmd for cmd in self.hidden_commands if cmd != 'py']
47+
48+
You might be thinking that the list comprehension is overkill and you'd rather
49+
do something like::
50+
51+
self.hidden_commands.remove('py')
52+
53+
You may be right, but ``remove()`` will raise a ``ValueError`` if ``py``
54+
isn't in the list, and it will only remove the first one if it's in the list
55+
multiple times.
56+
57+
58+
Disable A Command
59+
-----------------
60+
61+
One way to disable a command is to add code to the command method which
62+
determines whether the command should be executed or not. If the command should
63+
not be executed, your code can print an appropriate error message and return.
64+
65+
``cmd2`` also provides another way to accomplish the same thing. Here's a
66+
simple app which disables the ``open`` command if the door is locked::
67+
68+
class DisabledCommands(cmd2.Cmd):
69+
"""An application which disables and enables commands"""
70+
71+
def do_lock(self, line):
72+
self.disable_command('open', "you can't open the door because it is locked")
73+
self.poutput('the door is locked')
74+
75+
def do_unlock(self, line):
76+
self.enable_command('open')
77+
self.poutput('the door is unlocked')
78+
79+
def do_open(self, line):
80+
"""open the door"""
81+
self.poutput('opening the door')
82+
83+
This method has the added benefit of removing disabled commands from the help
84+
menu. But, this method only works if you know in advance that the command
85+
should be disabled, and if the conditions for re-enabling it are likewise known
86+
in advance.
87+
88+
89+
Disable A Category of Commands
90+
------------------------------
91+
92+
You can group or categorize commands as shown in
93+
:ref:`features/help:Categorizing Commands`. If you do so, you can disable and
94+
enable all the commands in a category with a single method call. Say you have
95+
created a category of commands called "Server Information". You can disable
96+
all commands in that category::
97+
98+
not_connected_msg = 'You must be connected to use this command'
99+
self.disable_category('Server Information', not_connected_msg)
100+
101+
Similarly, you can re-enable all the commands in a category::
102+
103+
self.enable_category('Server Information')

docs/features/help.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ Use ``help_method()`` to custom roll your own help messages.
77

88
See :ref:`features/argument_processing:Help Messages`
99

10-
Grouping Commands
11-
-----------------
10+
Categorizing Commands
11+
---------------------
1212

1313
By default, the ``help`` command displays::
1414

examples/remove_unused.py renamed to examples/remove_builtin_commands.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import cmd2
1313

1414

15-
class RemoveUnusedBuiltinCommands(cmd2.Cmd):
15+
class RemoveBuiltinCommands(cmd2.Cmd):
1616
""" Example cmd2 application where we remove some unused built-in commands."""
1717

1818
def __init__(self):
@@ -27,5 +27,5 @@ def __init__(self):
2727

2828
if __name__ == '__main__':
2929
import sys
30-
app = RemoveUnusedBuiltinCommands()
30+
app = RemoveBuiltinCommands()
3131
sys.exit(app.cmdloop())

0 commit comments

Comments
 (0)