Skip to content

Commit eaf2918

Browse files
authored
Merge pull request #593 from python-cmd2/document_completion
Improved README.md
2 parents bb9e5e5 + 2f27fdb commit eaf2918

File tree

4 files changed

+149
-37
lines changed

4 files changed

+149
-37
lines changed

README.md

Lines changed: 114 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Main Features
3737
- Parsing commands with arguments using `argparse`, including support for sub-commands
3838
- Unicode character support
3939
- Good tab-completion of commands, sub-commands, file system paths, and shell commands
40+
- Automatic tab-completion of `argparse` flags when using one of the `cmd2` `argparse` decorators
4041
- Support for Python 3.4+ on Windows, macOS, and Linux
4142
- Trivial to provide built-in help for all commands
4243
- Built-in regression testing framework for your applications (transcript-based testing)
@@ -76,33 +77,52 @@ Feature Overview
7677
----------------
7778
Instructions for implementing each feature follow.
7879

79-
- Searchable command history
80-
81-
All commands will automatically be tracked in the session's history, unless the command is listed in Cmd's exclude_from_history attribute.
82-
The history is accessed through the `history` command.
83-
If you wish to exclude some of your custom commands from the history, append their names
84-
to the list at `Cmd.exclude_from_history`.
85-
86-
- Load commands from file, save to file, edit commands in file
87-
88-
Type `help load`, `help history` for details.
89-
90-
- Multi-line commands
91-
92-
Any command accepts multi-line input when its name is listed in `Cmd.multiline_commands`.
93-
The program will keep expecting input until a line ends with any of the characters
94-
in `Cmd.terminators` . The default terminators are `;` and `/n` (empty newline).
95-
96-
- Special-character shortcut commands (beyond cmd's "@" and "!")
97-
98-
To create a single-character shortcut for a command, update `Cmd.shortcuts`.
99-
100-
- Settable environment parameters
80+
- Extension of the `cmd` module. So capabilities provided by `cmd` still exist
81+
- Your applicaiton inherits from `cmd2.Cmd`, let's say you call this class `MyApp`
82+
```Python
83+
import cmd2
84+
class MyApp(cmd2.Cmd):
85+
pass
86+
```
87+
- Define a command named **foo** by creating a method named **do_foo**
88+
```Python
89+
class MyApp(cmd2.Cmd):
90+
def do_foo(self, args):
91+
"""This docstring is the built-in help for the foo command."""
92+
print('foo bar baz')
93+
```
94+
- By default the docstring for your **do_foo** method is the help for the **foo** command
95+
- NOTE: This doesn't apply if you use one of the `argparse` decorators mentioned below
96+
- Can provide more custom help by creating a **help_foo** method (except when using `argparse` decorators)
97+
- Can provide custom tab-completion for the **foo** command by creating a **complete_foo** method
98+
- Easy to upgrade an existing `cmd` app to `cmd2`
99+
- Run your `cmd2` app using the built-in REPL by executing the **cmdloop** method
101100

102-
To allow a user to change an environment parameter during program execution,
103-
append the parameter's name to `Cmd.settable``
101+
- Searchable command history
102+
- Readline history using `<Ctrl>+r`, arrow keys, and other [Readline Shortcut keys](http://readline.kablamo.org/emacs.html)
103+
- Readline history can be persistent between application runs via optional argument to `cmd2.Cmd` initializer
104+
- `cmd2` `history` command provides flexible and powerful search
105+
- By design, this history does NOT persist between application runs
106+
- If you wish to exclude some of your custom commands from the history, append their names to the list at `Cmd.exclude_from_history`.
107+
- Do `help history` in any `cmd2` application for more information
108+
109+
- Simple scripting using ASCII text files with one command + arguments per line
110+
- See the [Script files](https://cmd2.readthedocs.io/en/latest/freefeatures.html#script-files) section of the `cmd2` docs for more info
111+
- See [script.txt](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/script.txt) for a trivial example script that can be
112+
used in any `cmd2` application with the `load` command (or `@` shortcut)
113+
114+
- Powerful and flexible built-in Python scripting of your application using the `pyscript` command
115+
- Run arbitrary Python scripts within your `cmd2` application with the ability to also call custom `cmd2` commands
116+
- No separate API for your end users to learn
117+
- Syntax for calling `cmd2` commands in a `pyscript` is essentially identical to what they would enter on the command line
118+
- See the [Python](https://cmd2.readthedocs.io/en/latest/freefeatures.html#python) section of the `cmd2` docs for more info
119+
- Also see the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py)
120+
example in conjunciton with the [conditional.py](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/conditional.py) script
104121

105122
- Parsing commands with `argparse`
123+
- Two decorators provide built-in capability for using `argparse.ArgumentParser` to parse command arguments
124+
- `cmd2.with_argparser` - all arguments are parsed by the `ArgumentParser`
125+
- `cmd2.with_argparser_and_unknown_args` - any arguments not parsed by the `ArgumentParser` get passed as a list
106126

107127
```Python
108128
import argparse
@@ -126,6 +146,61 @@ Instructions for implementing each feature follow.
126146
```
127147

128148
See https://cmd2.readthedocs.io/en/latest/argument_processing.html for more details
149+
150+
NOTE: `cmd2` also provides the `ACArgumentParser` customization of `argparse.ArgumentParser` for prettier formatting
151+
of help and RangeAction type
152+
153+
- `cmd2` applications function like a full-featured shell in many ways (and are cross-platform)
154+
- Run arbitrary shell commands by preceding them with `!` or `shell`
155+
- Redirect the output of any command to a file with `>` for overwrite or `>>` for append
156+
- If no file name provided after the `>`/`>>`, then output goes to the clipboard/pastebuffer
157+
- Pipe the output of any command to an arbitrary shell command with `|`
158+
- Create your own custom command aliases using the `alias` command
159+
- Create your own custom macros using the `macro` command (similar to aliases, but allow arguments)
160+
- Settable environment parameters that users can change during execution supported via `set` command
161+
- Option to display long output using a pager with ``cmd2.Cmd.ppaged()``
162+
- Optionally specify a startup script that end users can use to customize their environment
163+
164+
- Top-notch tab-completion capabilities which are easy to use but very powerful
165+
- For a command **foo** implement a **complete_foo** method to provide custom tab completion for that command
166+
- But the helper methods within `cmd2` discussed below mean you would rarely have to implement this from scratch
167+
- Commands which use one of the `argparse` decorators have automatic tab-completion of `argparse` flags
168+
- And also provide help hints for values associated with these flags
169+
- Experiment with the [argprint.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) example
170+
using the **oprint** and **pprint** commands to get a feel for how this works
171+
- `path_complete` helper method provides flexible tab-completion of file system paths
172+
- See the [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) example for a simple use case
173+
- See the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example for a more full-featured use case
174+
- `flag_based_complete` helper method for tab completion based on a particular flag preceding the token being completed
175+
- See the [tab_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_completion.py) example for a demonstration of how to use this feature
176+
- `index_based_complete` helper method for tab completion based on a fixed position in the input string
177+
- See the [tab_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_completion.py) example for a demonstration of how to use this feature
178+
- `basic_complete` helper method for tab completion against a list
179+
- `delimiter_complete` helper method for tab completion against a list but each match is split on a delimiter
180+
- See the [tab_autocompletion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py) example for a demonstration of how to use this feature
181+
- `cmd2` in combination with `argparse` also provide several advanced capabilities for automatic tab-completion
182+
- See the [tab_autocompletion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py) and
183+
[tab_autocomp_dynamic.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocomp_dynamic.py) examples for more info
184+
185+
- Multi-line commands
186+
187+
Any command accepts multi-line input when its name is listed in `Cmd.multiline_commands`.
188+
The program will keep expecting input until a line ends with any of the characters
189+
in `Cmd.terminators` . The default terminators are `;` and `/n` (empty newline).
190+
191+
- Special-character shortcut commands (beyond cmd's "@" and "!")
192+
193+
To create a single-character shortcut for a command, update `Cmd.shortcuts`.
194+
195+
- Asynchronous alerts based on events happening in background threads
196+
- `cmd2` provides the following helper methods for providing information to users asynchronously even though the `cmd2`
197+
REPL is a line-oriented command interpreter:
198+
- `async_alert` - display an important message to the user while they are at the prompt in between commands
199+
- To the user it appears as if an alert message is printed above the prompt
200+
- `async_update_prompt` - update the prompt while the user is still typing at it
201+
- This is good for alerting the user to system changes dynamically in between commands
202+
- `set_window_title` - set the terminal window title
203+
- This changes the window title of the terminal that the user is running the `cmd2` app within
129204

130205

131206
Tutorials
@@ -267,3 +342,18 @@ If you think you've found a bug, please first read through the open [Issues](htt
267342
* OS name and version
268343
* What you did to cause the bug to occur
269344
* Include any traceback or error message associated with the bug
345+
346+
347+
Open source projects using cmd2
348+
-------------------------------
349+
350+
Here are a few examples of open-source projects which use `cmd2`:
351+
352+
* [cliff](https://github.com/openstack/cliff)
353+
* OpenStack Command Line Interface Formulation Framework
354+
* [tomcatmanager](https://github.com/tomcatmanager/tomcatmanager)
355+
* A command line tool and python library for managing a tomcat server
356+
* [clanvas](https://github.com/marklalor/clanvas)
357+
* Command-line client for Canvas by Instructure
358+
* [mptcpanalyzer](https://github.com/teto/mptcpanalyzer)
359+
* Tool to help analyze mptcp pcaps

cmd2/cmd2.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,16 @@ def cat_decorator(func):
173173
return cat_decorator
174174

175175

176-
def with_argument_list(func: Callable, preserve_quotes: bool=False) -> Callable:
177-
"""A decorator to alter the arguments passed to a do_* cmd2
178-
method. Default passes a string of whatever the user typed.
179-
With this decorator, the decorated method will receive a list
180-
of arguments parsed from user input using shlex.split()."""
176+
def with_argument_list(func: Callable[[Statement], Optional[bool]],
177+
preserve_quotes: bool=False) -> Callable[[List], Optional[bool]]:
178+
"""A decorator to alter the arguments passed to a do_* cmd2 method. Default passes a string of whatever the user
179+
typed. With this decorator, the decorated method will receive a list of arguments parsed from user input using
180+
shlex.split().
181+
182+
:param func: do_* method this decorator is wrapping
183+
:param preserve_quotes: if True, then argument quotes will not be stripped
184+
:return: function that gets passed a list of argument strings
185+
"""
181186
import functools
182187

183188
@functools.wraps(func)
@@ -189,18 +194,19 @@ def cmd_wrapper(self, cmdline):
189194
return cmd_wrapper
190195

191196

192-
def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, preserve_quotes: bool=False) -> Callable:
197+
def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, preserve_quotes: bool=False) -> \
198+
Callable[[argparse.Namespace, List], Optional[bool]]:
193199
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given
194200
instance of argparse.ArgumentParser, but also returning unknown args as a list.
195201
196202
:param argparser: unique instance of ArgumentParser
197-
:param preserve_quotes: if True, then the arguments passed to arparse be maintain their quotes
198-
:return: function that gets passed parsed args and a list of unknown args
203+
:param preserve_quotes: if True, then arguments passed to argparse maintain their quotes
204+
:return: function that gets passed argparse-parsed args and a list of unknown argument strings
199205
"""
200206
import functools
201207

202208
# noinspection PyProtectedMember
203-
def arg_decorator(func: Callable):
209+
def arg_decorator(func: Callable[[Statement], Optional[bool]]):
204210
@functools.wraps(func)
205211
def cmd_wrapper(instance, cmdline):
206212
lexed_arglist = parse_quoted_string(cmdline, preserve_quotes)
@@ -230,18 +236,19 @@ def cmd_wrapper(instance, cmdline):
230236
return arg_decorator
231237

232238

233-
def with_argparser(argparser: argparse.ArgumentParser, preserve_quotes: bool=False) -> Callable:
239+
def with_argparser(argparser: argparse.ArgumentParser,
240+
preserve_quotes: bool=False) -> Callable[[argparse.Namespace], Optional[bool]]:
234241
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
235242
with the given instance of argparse.ArgumentParser.
236243
237244
:param argparser: unique instance of ArgumentParser
238-
:param preserve_quotes: if True, then the arguments passed to arparse be maintain their quotes
239-
:return: function that gets passed parsed args
245+
:param preserve_quotes: if True, then arguments passed to argparse maintain their quotes
246+
:return: function that gets passed the argparse-parsed args
240247
"""
241248
import functools
242249

243250
# noinspection PyProtectedMember
244-
def arg_decorator(func: Callable):
251+
def arg_decorator(func: Callable[[Statement], Optional[bool]]):
245252
@functools.wraps(func)
246253
def cmd_wrapper(instance, cmdline):
247254
lexed_arglist = parse_quoted_string(cmdline, preserve_quotes)

docs/argument_processing.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ processing decorators in your ``cmd2`` applications.
2121
.. _argprint: https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py
2222
.. _decorator: https://github.com/python-cmd2/cmd2/blob/master/examples/decorator_example.py
2323

24+
25+
Decorators provided by cmd2 for argument processing
26+
===================================================
27+
``cmd2`` provides the following decorators for assisting with parsing arguments passed to commands:
28+
29+
.. automethod:: cmd2.cmd2.with_argument_list
30+
.. automethod:: cmd2.cmd2.with_argparser
31+
.. automethod:: cmd2.cmd2.with_argparser_and_unknown_args
32+
33+
All of these decorators accept an optional **preserve_quotes** argument which defaults to ``False``.
34+
Setting this argument to ``True`` is useful for cases where you are passing the arguments to another
35+
command which might have its own argument parsing.
36+
37+
2438
Using the argument parser decorator
2539
===================================
2640

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- Parsing commands with arguments using `argparse`, including support for sub-commands
3535
- Unicode character support
3636
- Good tab-completion of commands, sub-commands, file system paths, and shell commands
37+
- Automatic tab-completion of `argparse` flags when using one of the `cmd2` `argparse` decorators
3738
- Support for Python 3.4+ on Windows, macOS, and Linux
3839
- Trivial to provide built-in help for all commands
3940
- Built-in regression testing framework for your applications (transcript-based testing)

0 commit comments

Comments
 (0)