From 746005566863638d98fc390e1c36ce7c18b77b5c Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 17:56:25 -0400 Subject: [PATCH 01/18] Starting to give the documentation a very thorough review --- docs/features/argument_processing.md | 55 ++++++++++++++++------------ docs/features/builtin_commands.md | 46 +++++++++++------------ docs/features/clipboard.md | 10 +---- docs/features/commands.md | 45 ++++++++++++----------- docs/features/completion.md | 25 +++++++------ docs/index.md | 4 +- docs/migrating/incompatibilities.md | 35 +++++++++--------- docs/migrating/minimum.md | 8 ++-- docs/migrating/next_steps.md | 24 ++++++------ docs/migrating/why.md | 38 ++++++++++--------- docs/overview/alternatives.md | 15 +++++--- docs/overview/installation.md | 40 +++++++++++++------- docs/overview/integrating.md | 17 +++++---- docs/overview/resources.md | 4 -- 14 files changed, 194 insertions(+), 172 deletions(-) diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index 5f3392d16..b65f4ad94 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -4,15 +4,20 @@ [argparse](https://docs.python.org/3/library/argparse.html) python module. `cmd2` handles the following for you: -1. Parsing input and quoted strings like the Unix shell -1. Parse the resulting argument list using an instance of `argparse.ArgumentParser` that you provide -1. Passes the resulting `argparse.Namespace` object to your command function. The `Namespace` - includes the `Statement` object that was created when parsing the command line. It can be - retrieved by calling `cmd2_statement.get()` on the `Namespace`. +1. Parsing input and quoted strings in a manner similar to how POSIX shells do it +1. Parse the resulting argument list using an instance of + [argparse.ArgumentParser](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser) + that you provide +1. Passes the resulting + [argparse.Namespace](https://docs.python.org/3/library/argparse.html#argparse.Namespace) object + to your command function. The `Namespace` includes the [Statement][cmd2.Statement] object that + was created when parsing the command line. It can be retrieved by calling `cmd2_statement.get()` + on the `Namespace`. 1. Adds the usage message from the argument parser to your command's help. 1. Checks if the `-h/--help` option is present, and if so, displays the help message for the command -These features are all provided by the `@with_argparser` decorator which is imported from `cmd2`. +These features are all provided by the [@with_argparser][cmd2.with_argparser] decorator which is +imported from `cmd2`. See the [argparse_example](https://github.com/python-cmd2/cmd2/blob/main/examples/argparse_example.py) @@ -31,11 +36,11 @@ command which might have its own argument parsing. ## Argument Parsing -For each command in the `cmd2` subclass which requires argument parsing, create a unique instance of -`argparse.ArgumentParser()` which can parse the input appropriately for the command. Then decorate -the command method with the `@with_argparser` decorator, passing the argument parser as the first -parameter to the decorator. This changes the second argument to the command method, which will -contain the results of `ArgumentParser.parse_args()`. +For each command in the `cmd2.Cmd` subclass which requires argument parsing, create a unique +instance of `argparse.ArgumentParser()` which can parse the input appropriately for the command. +Then decorate the command method with the `@with_argparser` decorator, passing the argument parser +as the first parameter to the decorator. This changes the second argument to the command method, +which will contain the results of `ArgumentParser.parse_args()`. Here's what it looks like: @@ -65,8 +70,8 @@ def do_speak(self, opts): `cmd2` sets the `prog` variable in the argument parser based on the name of the method it is decorating. This will override anything you specify in `prog` variable when creating the argument parser. -As of the 3.0.0 release, `cmd2` sets `prog` when the instance-specific parser is created, which is -later than it did previously. + As of the 3.0.0 release, `cmd2` sets `prog` when the instance-specific parser is created, which is + later than it did previously. ## Help Messages @@ -84,7 +89,7 @@ argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): - """create a html tag""" + """Create an HTML tag""" self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') ``` @@ -94,7 +99,7 @@ the `help tag` command displays: ```text usage: tag [-h] tag content [content ...] -create an HTML tag +Create an HTML tag positional arguments: tag tag @@ -168,13 +173,13 @@ This command cannot generate tags with no content, like
!!! warning - If a command **foo** is decorated with one of cmd2's argparse decorators, then **help_foo** will not be invoked when `help foo` is called. The [argparse](https://docs.python.org/3/library/argparse.html) module provides a rich API which can be used to tweak every aspect of the displayed help and we encourage `cmd2` developers to utilize that. + If a command **foo** is decorated with `cmd2`'s `with_argparse` decorator, then **help_foo** will not be invoked when `help foo` is called. The [argparse](https://docs.python.org/3/library/argparse.html) module provides a rich API which can be used to tweak every aspect of the displayed help and we encourage `cmd2` developers to utilize that. ## Argument List The default behavior of `cmd2` is to pass the user input directly to your `do_*` methods as a -string. The object passed to your method is actually a `Statement` object, which has additional -attributes that may be helpful, including `arg_list` and `argv`: +string. The object passed to your method is actually a [Statement][cmd2.Statement] object, which has +additional attributes that may be helpful, including `arg_list` and `argv`: ```py class CmdLineApp(cmd2.Cmd): @@ -200,8 +205,8 @@ class CmdLineApp(cmd2.Cmd): If you don't want to access the additional attributes on the string passed to your `do_*` method you can still have `cmd2` apply shell parsing rules to the user input and pass you a list of arguments -instead of a string. Apply the `@with_argument_list` decorator to those methods that should receive -an argument list instead of a string: +instead of a string. Apply the [@with_argument_list][cmd2.with_argument_list] decorator to those +methods that should receive an argument list instead of a string: ```py from cmd2 import with_argument_list @@ -272,7 +277,7 @@ def settings_ns_provider(self) -> argparse.Namespace: return ns ``` -To use this function with the argparse decorators, do the following: +To use this function with the `@2ith_argparser` decorator, do the following: ```py @with_argparser(my_parser, ns_provider=settings_ns_provider) @@ -293,6 +298,8 @@ See the [argparse_example](https://github.com/python-cmd2/cmd2/blob/main/examples/argparse_example.py) example to learn more about how to use subcommands in your `cmd2` application. +The [@as_subcommand_to][cmd2.as_subcommand_to] decorator makes adding subcommands easy. + ## Argparse Extensions `cmd2` augments the standard `argparse.nargs` with range tuple capability: @@ -300,7 +307,7 @@ example to learn more about how to use subcommands in your `cmd2` application. - `nargs=(5,)` - accept 5 or more items - `nargs=(8, 12)` - accept 8 to 12 items -`cmd2` also provides the `cmd2.argparse_custom.Cmd2ArgumentParser` class which inherits from +`cmd2` also provides the [Cmd2ArgumentParser][cmd2.Cmd2ArgumentParser] class which inherits from `argparse.ArgumentParser` and improves error and help output. ## Decorator Order @@ -338,8 +345,8 @@ example demonstrates both above cases in a concrete fashion. ## Reserved Argument Names -`cmd2` argparse decorators add the following attributes to argparse Namespaces. To avoid naming -collisions, do not use any of the names for your argparse arguments. +`cmd2`'s `@with_argparser` decorator adds the following attributes to argparse Namespaces. To avoid +naming collisions, do not use any of the names for your argparse arguments. - `cmd2_statement` - `cmd2.Cmd2AttributeWrapper` object containing the `cmd2.Statement` object that was created when parsing the command line. diff --git a/docs/features/builtin_commands.md b/docs/features/builtin_commands.md index becc4cd31..7deed08ba 100644 --- a/docs/features/builtin_commands.md +++ b/docs/features/builtin_commands.md @@ -1,6 +1,6 @@ # Builtin Commands -Applications which subclass `cmd2.Cmd` inherit a number of commands which may be useful to your +Applications which subclass [cmd2.Cmd][] inherit a number of commands which may be useful to your users. Developers can [Remove Builtin Commands](#remove-builtin-commands) if they do not want them to be part of the application. @@ -33,7 +33,7 @@ for more information. This command allows you to view, run, edit, save, or clear previously entered commands from the history. See [History](history.md) for more information. -### ipy +### ipy (optional) This optional opt-in command enters an interactive IPython shell. See [IPython (optional)](./embedded_python_shells.md#ipython-optional) for more information. @@ -44,9 +44,9 @@ This command manages macros via subcommands `create`, `delete`, and `list`. A ma alias, but it can contain argument placeholders. See [Macros](./shortcuts_aliases_macros.md#macros) for more information. -### py +### py (optional) -This command invokes a Python command or shell. See +This optional opt-in command invokes a Python command or shell. See [Embedded Python Shells](./embedded_python_shells.md) for more information. ### quit @@ -63,7 +63,7 @@ This command runs a Python script file inside the `cmd2` application. See This command runs commands in a script file that is encoded as either ASCII or UTF-8 text. See [Command Scripts](./scripting.md#command-scripts) for more information. -### \_relative_run_script +### \_relative_run_script (hidden) This command is hidden from the help that's visible to end users. It runs a script like [run_script](#run_script) but does so using a path relative to the script that is currently @@ -77,21 +77,19 @@ application: ```text (Cmd) set -Name Value Description -==================================================================================================================== -allow_style Terminal Allow ANSI text style sequences in output (valid values: - Always, Never, Terminal) -always_show_hint False Display tab completion hint even when completion suggestions - print -debug True Show full traceback on exception -echo False Echo command issued into output -editor vi Program used by 'edit' -feedback_to_output False Include nonessentials in '|' and '>' results -max_completion_items 50 Maximum number of CompletionItems to display during tab - completion -quiet False Don't print non-essential feedback -scripts_add_to_history True Scripts and pyscripts add commands to history -timing False Report execution times + Name Value Description +─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + allow_style Terminal Allow ANSI text style sequences in output (valid values: Always, Never, Terminal) + always_show_hint False Display tab completion hint even when completion suggestions print + debug False Show full traceback on exception + echo False Echo command issued into output + editor vim Program used by 'edit' + feedback_to_output False Include nonessentials in '|' and '>' results + foreground_color cyan Foreground color to use with echo command + max_completion_items 50 Maximum number of CompletionItems to display during tab completion + quiet False Don't print nonessential feedback + scripts_add_to_history True Scripts and pyscripts add commands to history + timing False Report execution times ``` Any of these user-settable parameters can be set while running your app with the `set` command like @@ -119,10 +117,10 @@ more information. ## Remove Builtin Commands -Developers may not want to offer the commands builtin to [cmd2.Cmd][] to users of their application. -To remove a command you must delete the method implementing that command from the [cmd2.Cmd][] -object at runtime. For example, if you wanted to remove the [shell](#shell) command from your -application: +Developers may not want to offer all the commands built into [cmd2.Cmd][] to users of their +application. To remove a command you must delete the method implementing that command from the +[cmd2.Cmd][] object at runtime. For example, if you wanted to remove the [shell](#shell) command +from your application: ```py class NoShellApp(cmd2.Cmd): diff --git a/docs/features/clipboard.md b/docs/features/clipboard.md index f3d118565..355461731 100644 --- a/docs/features/clipboard.md +++ b/docs/features/clipboard.md @@ -1,8 +1,8 @@ # Clipboard Integration Nearly every operating system has some notion of a short-term storage area which can be accessed by -any program. Usually this is called the clipboard, but sometimes people refer to it as the paste -buffer. +any program. Usually this is called the :clipboard: clipboard, but sometimes people refer to it as +the paste buffer. `cmd2` integrates with the operating system clipboard using the [pyperclip](https://github.com/asweigart/pyperclip) module. Command output can be sent to the @@ -32,10 +32,4 @@ If you would like your `cmd2` based application to be able to use the clipboard alternative ways, you can use the following methods (which work uniformly on Windows, macOS, and Linux). - ::: cmd2.clipboard -handler: python -options: -show_root_heading: false -show_source: false - diff --git a/docs/features/commands.md b/docs/features/commands.md index ef6cb04b2..acf538899 100644 --- a/docs/features/commands.md +++ b/docs/features/commands.md @@ -28,16 +28,16 @@ if __name__ == '__main__': sys.exit(c.cmdloop()) ``` -This application subclasses `cmd2.Cmd` but has no code of its own, so all functionality (and there's -quite a bit) is inherited. Let's create a simple command in this application called `echo` which -outputs any arguments given to it. Add this method to the class: +This application subclasses [cmd2.Cmd][] but has no code of its own, so all functionality (and +there's quite a bit) is inherited. Let's create a simple command in this application called `echo` +which outputs any arguments given to it. Add this method to the class: ```py def do_echo(self, line): self.poutput(line) ``` -When you type input into the `cmd2` prompt, the first space delimited word is treated as the command +When you type input into the `cmd2` prompt, the first space-delimited word is treated as the command name. `cmd2` looks for a method called `do_commandname`. If it exists, it calls the method, passing the rest of the user input as the first argument. If it doesn't exist `cmd2` prints an error message. As a result of this behavior, the only thing you have to do to create a new command is to @@ -47,13 +47,14 @@ python standard library. !!! note - See [Generating Output](./generating_output.md) if you are unfamiliar with the `poutput()` method. + See [Generating Output](./generating_output.md) if you are unfamiliar with the + [poutput()][cmd2.Cmd.poutput] method. ## Statements A command is passed one argument: a string which contains all the rest of the user input. However, -in `cmd2` this string is actually a `Statement` object, which is a subclass of `str` to retain -backwards compatibility. +in `cmd2` this string is actually a [Statement][cmd2.Statement] object, which is a subclass of `str` +to retain backwards compatibility with `cmd`. `cmd2` has a much more sophisticated parsing engine than what's included in the [cmd](https://docs.python.org/3/library/cmd.html) module. This parsing handles: @@ -84,7 +85,7 @@ args commands removed. It turns out that the "string" value of the `Statement` object has all the output redirection and piping clauses removed as well. Quotes remain in the string. -command[and_args]{#and_args} +command_and_args : A string of just the command and the arguments, with output redirection or piping to shell commands removed. @@ -116,9 +117,11 @@ Most commands should return nothing (either by omitting a `return` statement, or This indicates that your command is finished (with or without errors), and that `cmd2` should prompt the user for more input. -If you return `True` from a command method, that indicates to `cmd2` that it should stop prompting -for user input and cleanly exit. `cmd2` already includes a `quit` command, but if you wanted to make -another one called `finish` you could: +If you return `True` or any +[Truthy](https://www.freecodecamp.org/news/truthy-and-falsy-values-in-python/) value from a command +method, that indicates to `cmd2` that it should stop prompting for user input and cleanly exit. +`cmd2` already includes a `quit` command, but if you wanted to make another one called `finish` you +could: ```py def do_finish(self, line): @@ -128,11 +131,11 @@ def do_finish(self, line): ## Exit Codes -`cmd2` has basic infrastructure to support sh/ksh/csh/bash type exit codes. The `cmd2.Cmd` object -sets an `exit_code` attribute to zero when it is instantiated. The value of this attribute is -returned from the `cmdloop()` call. Therefore, if you don't do anything with this attribute in your -code, `cmdloop()` will (almost) always return zero. There are a few built-in `cmd2` commands which -set `exit_code` to `1` if an error occurs. +`cmd2` has basic infrastructure to support POSIX shell exit codes. The `cmd2.Cmd` object sets an +`exit_code` attribute to zero when it is instantiated. The value of this attribute is returned from +the `cmdloop()` call. Therefore, if you don't do anything with this attribute in your code, +`cmdloop()` will (almost) always return zero. There are a few built-in `cmd2` commands which set +`exit_code` to `1` if an error occurs. You can use this capability to easily return your own values to the operating system shell: @@ -192,13 +195,13 @@ All other `BaseExceptions` are not caught by `cmd2` and will be raised. See [Disabling Commands](./disable_commands.md) for details of how to: -- remove commands included in `cmd2` -- hide commands from the help menu -- disable and re-enable commands at runtime +- Remove commands included in `cmd2` +- Hide commands from the help menu +- Dynamically disable and re-enable commands at runtime ## Modular Commands and Loading/Unloading Commands See [Modular Commands](./modular_commands.md) for details of how to: -- Define commands in separate CommandSet modules -- Load or unload commands at runtime +- Define commands in separate [CommandSet][cmd2.CommandSet] modules +- Dynamically load or unload commands at runtime diff --git a/docs/features/completion.md b/docs/features/completion.md index 254a9ac53..e56056131 100644 --- a/docs/features/completion.md +++ b/docs/features/completion.md @@ -35,11 +35,12 @@ complete_bar = functools.partialmethod(cmd2.Cmd.path_complete, path_filter=os.pa ## Included Tab Completion Functions -`cmd2` provides the following tab completion functions +[cmd2.Cmd][] provides the following tab completion functions -- `cmd2.Cmd.basic_complete` - helper method for tab completion against a list +- [basic_complete][cmd2.Cmd.basic_complete] - helper method for tab completion against a list -- `cmd2.Cmd.path_complete` - helper method provides flexible tab completion of file system paths +- [path_complete][cmd2.Cmd.path_complete] - helper method provides flexible tab completion of file + system paths > - See the > [paged_output](https://github.com/python-cmd2/cmd2/blob/main/examples/paged_output.py) @@ -48,18 +49,18 @@ complete_bar = functools.partialmethod(cmd2.Cmd.path_complete, path_filter=os.pa > [python_scripting](https://github.com/python-cmd2/cmd2/blob/main/examples/python_scripting.py) > example for a more full-featured use case -- `cmd2.Cmd.delimiter_complete` - helper method for tab completion against a list but each match is - split on a delimiter +- [delimiter_complete][cmd2.Cmd.delimiter_complete] - helper method for tab completion against a + list but each match is split on a delimiter > - See the > [basic_completion](https://github.com/python-cmd2/cmd2/blob/main/examples/basic_completion.py) > example for a demonstration of how to use this feature -- `cmd2.Cmd.flag_based_complete` - helper method for tab completion based on a particular flag - preceding the token being completed +- [flag_based_complete][cmd2.Cmd.flag_based_complete] - helper method for tab completion based on a + particular flag preceding the token being completed -- `cmd2.Cmd.index_based_complete` - helper method for tab completion based on a fixed position in - the input string +- [index_based_complete][cmd2.Cmd.index_based_complete] - helper method for tab completion based on + a fixed position in the input string > - See the > [basic_completion](https://github.com/python-cmd2/cmd2/blob/main/examples/basic_completion.py) @@ -83,10 +84,10 @@ error occurs in which it is more desirable to display a message than a stack tra has a member called `apply_style`. Set this False if the error style should not be applied. For instance, `ArgparseCompleter` sets it to False when displaying completion hints. -## Tab Completion Using argparse Decorators {: #argparse-based } +## Tab Completion Using argparse Decorator {: #argparse-based } -When using one of the argparse-based [cmd2.decorators](../api/decorators.md), `cmd2` provides -automatic tab completion of flag names. +When using `cmd2`'s [@with_argparser][cmd2.with_argparser] decorator, `cmd2` provides automatic tab +completion of flag names. Tab completion of argument values can be configured by using one of three parameters to `argparse.ArgumentParser.add_argument` diff --git a/docs/index.md b/docs/index.md index 063af6c60..431ac2983 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,7 @@ # cmd2 -A Python package for building powerful command-line interpreter (CLI) programs. Extends the Python -Standard Library's [cmd](https://docs.python.org/3/library/cmd.html) package. +A :simple-python: Python package for building powerful command-line interpreter (CLI) programs. +Extends the Python Standard Library's [cmd](https://docs.python.org/3/library/cmd.html) package. The basic use of `cmd2` is identical to that of [cmd](https://docs.python.org/3/library/cmd.html). diff --git a/docs/migrating/incompatibilities.md b/docs/migrating/incompatibilities.md index 6ed3a0209..df9668c02 100644 --- a/docs/migrating/incompatibilities.md +++ b/docs/migrating/incompatibilities.md @@ -1,7 +1,6 @@ # Incompatibilities -`cmd2` strives to be drop-in compatible with [cmd](https://docs.python.org/3/library/cmd.html), -however there are a few incompatibilities. +`cmd2` strives to be drop-in compatible with [cmd][cmd], however there are a few incompatibilities. ## Cmd.emptyline() @@ -14,20 +13,18 @@ behavior cannot be overridden. ## Cmd.identchars -In [cmd](https://docs.python.org/3/library/cmd.html), the -[Cmd.identchars](https://docs.python.org/3/library/cmd.html#cmd.Cmd.identchars) attribute contains -the string of characters accepted for command names. -[cmd](https://docs.python.org/3/library/cmd.html) uses those characters to split the first "word" of -the input, without requiring the user to type a space. For example, if `identchars` contained a -string of all alphabetic characters, the user could enter a command like `L20` and it would be -interpreted as the command `L` with the first argument of `20`. +In [cmd][cmd], the [Cmd.identchars](https://docs.python.org/3/library/cmd.html#cmd.Cmd.identchars) +attribute contains the string of characters accepted for command names. [cmd][cmd] uses those +characters to split the first "word" of the input, without requiring the user to type a space. For +example, if `identchars` contained a string of all alphabetic characters, the user could enter a +command like `L20` and it would be interpreted as the command `L` with the first argument of `20`. Since version 0.9.0, `cmd2` has ignored `identchars`; the parsing logic in `cmd2` splits the command -and arguments on whitespace. We opted for this breaking change because while -[cmd](https://docs.python.org/3/library/cmd.html) supports unicode, using non-ascii unicode -characters in command names while simultaneously using `identchars` functionality can be somewhat -painful. Requiring white space to delimit arguments also ensures reliable operation of many other -useful `cmd2` features, including [Tab Completion](../features/completion.md) and +and arguments on whitespace. We opted for this breaking change because while [cmd][cmd] supports +unicode, using non-ascii unicode characters in command names while simultaneously using `identchars` +functionality can be somewhat painful. Requiring white space to delimit arguments also ensures +reliable operation of many other useful `cmd2` features, including +[Tab Completion](../features/completion.md) and [Shortcuts, Aliases, and Macros](../features/shortcuts_aliases_macros.md). If you really need this functionality in your app, you can add it back in by writing a @@ -35,12 +32,14 @@ If you really need this functionality in your app, you can add it back in by wri ## Cmd.cmdqueue -In [cmd](https://docs.python.org/3/library/cmd.html), the -[Cmd.cmdqueue](https://docs.python.org/3/library/cmd.html#cmd.Cmd.cmdqueue) attribute contains a -list of queued input lines. The cmdqueue list is checked in `cmdloop()` when new input is needed; if -it is nonempty, its elements will be processed in order, as if entered at the prompt. +In [cmd][cmd], the [Cmd.cmdqueue](https://docs.python.org/3/library/cmd.html#cmd.Cmd.cmdqueue) +attribute contains a list of queued input lines. The cmdqueue list is checked in `cmdloop()` when +new input is needed; if it is nonempty, its elements will be processed in order, as if entered at +the prompt. Since version 0.9.13 `cmd2` has removed support for `Cmd.cmdqueue`. Because `cmd2` supports running commands via the main `cmdloop()`, text scripts, Python scripts, transcripts, and history replays, the only way to preserve consistent behavior across these methods was to eliminate the command queue. Additionally, reasoning about application behavior is much easier without this queue present. + +[cmd]: https://docs.python.org/3/library/cmd diff --git a/docs/migrating/minimum.md b/docs/migrating/minimum.md index 6e06379ef..9eb8532d3 100644 --- a/docs/migrating/minimum.md +++ b/docs/migrating/minimum.md @@ -1,7 +1,9 @@ # Minimum Required Changes -`cmd2.Cmd` subclasses `Cmd.cmd` from the standard library, and overrides most of the methods. Most -apps based on the standard library can be migrated to `cmd2` in just a couple of minutes. +[cmd2.Cmd][] subclasses [cmd.Cmd](https://docs.python.org/3/library/cmd.html#cmd.Cmd) from the +standard library, and overrides all of the methods other than `Cmd.emptyline` (`cmd2` never calls +this method). Most apps based on the standard library can be migrated to `cmd2` in just a couple of +minutes. ## Import and Inheritance @@ -41,6 +43,6 @@ application, you may be able to remove them. See [Exiting](../features/misc.md#e If you are distributing your application, you'll also need to ensure that `cmd2` is properly installed. You will need to add the following dependency to your `pyproject.toml` or `setup.py`: - 'cmd2>=2,<3' + 'cmd2>=2.7' See [Integrate cmd2 Into Your Project](../overview/integrating.md) for more details. diff --git a/docs/migrating/next_steps.md b/docs/migrating/next_steps.md index 1e047a524..cff4913c5 100644 --- a/docs/migrating/next_steps.md +++ b/docs/migrating/next_steps.md @@ -7,19 +7,18 @@ leveraging other `cmd2` features. The three ideas here will get you started. Bro ## Argument Parsing For all but the simplest of commands, it's probably easier to use -[argparse](https://docs.python.org/3/library/argparse.html) to parse user input. `cmd2` provides a -`@with_argparser()` decorator which associates an `ArgumentParser` object with one of your commands. -Using this method will: +[argparse](https://docs.python.org/3/library/argparse.html) to parse user input than to do it +manually yourself for each command. `cmd2` provides a `@with_argparser()` decorator which associates +an `ArgumentParser` object with one of your commands. Using this method will: 1. Pass your command a [Namespace](https://docs.python.org/3/library/argparse.html#argparse.Namespace) containing the - arguments instead of a string of text. -2. Properly handle quoted string input from your users. -3. Create a help message for you based on the `ArgumentParser`. -4. Give you a big head start adding [Tab Completion](../features/completion.md) to your - application. + arguments instead of a string of text +2. Properly handle quoted string input from your users +3. Create a help message for you based on the `ArgumentParser` +4. Give you a big head start adding [Tab Completion](../features/completion.md) to your application 5. Make it much easier to implement subcommands (i.e. `git` has a bunch of subcommands such as - `git pull`, `git diff`, etc). + `git pull`, `git diff`, etc) There's a lot more about [Argument Processing](../features/argument_processing.md) if you want to dig in further. @@ -41,6 +40,7 @@ If your program generates output by printing directly to `sys.stdout`, you shoul to `cmd2.Cmd.poutput`, `cmd2.Cmd.perror`, and `cmd2.Cmd.pfeedback`. These methods work with several of the built in [Settings](../features/settings.md) to allow the user to view or suppress feedback (i.e. progress or status output). They also properly handle ANSI colored output according to user -preference. `cmd2`'s dependency on `rich` makes it easy to add color and style to your output. See -the [Colored Output](../features/generating_output.md#colored-output) section for more details. -These and other related topics are covered in [Generating Output](../features/generating_output.md). +preference. `cmd2`'s dependency on :simple-rich: [rich](https://github.com/Textualize/rich) makes it +easy to add color and style to your output. See the +[Colored Output](../features/generating_output.md#colored-output) section for more details. These +and other related topics are covered in [Generating Output](../features/generating_output.md). diff --git a/docs/migrating/why.md b/docs/migrating/why.md index 6d647182e..f8f2ff6df 100644 --- a/docs/migrating/why.md +++ b/docs/migrating/why.md @@ -2,44 +2,46 @@ ## cmd -[cmd](#cmd) is the Python Standard Library's module for creating simple interactive command-line -applications. [cmd](#cmd) is an extremely bare-bones framework which leaves a lot to be desired. It +[cmd][cmd] is the Python Standard Library's module for creating simple interactive command-line +applications. [cmd][cmd] is an extremely bare-bones framework which leaves a lot to be desired. It doesn't even include a built-in way to exit from an application! -Since the API provided by [cmd](#cmd) provides the foundation on which `cmd2` is based, -understanding the use of [cmd](#cmd) is the first step in learning the use of `cmd2`. Once you have -read the [cmd](#cmd) docs, return here to learn the ways that `cmd2` differs from [cmd](#cmd). +Since the API provided by [cmd][cmd] provides the foundation on which `cmd2` is based, understanding +the use of [cmd][cmd] is the first step in learning the use of `cmd2`. Once you have read the +[cmd](#cmd) docs, return here to learn the ways that `cmd2` differs from [cmd][cmd]. ## cmd2 -`cmd2` is a batteries-included extension of [cmd](#cmd), which provides a wealth of functionality to +`cmd2` is a batteries-included extension of [cmd][cmd], which provides a wealth of functionality to make it quicker and easier for developers to create feature-rich interactive command-line applications which delight customers. -`cmd2` can be used as a drop-in replacement for [cmd](#cmd) with a few minor discrepancies as +`cmd2` can be used as a drop-in replacement for [cmd][cmd] with a few minor discrepancies as discussed in the [Incompatibilities](incompatibilities.md) section. Simply importing `cmd2` in place -of [cmd](#cmd) will add many features to an application without any further modifications. Migrating +of [cmd][cmd] will add many features to an application without any further modifications. Migrating to `cmd2` will also open many additional doors for making it possible for developers to provide a top-notch interactive command-line experience for their users. -## Free Features +## Automatic Features -After switching from [cmd](#cmd) to `cmd2`, your application will have the following new features -and capabilities, without you having to do anything: +After switching from [cmd][cmd] to `cmd2`, your application will have the following new features and +capabilities, without you having to do anything: -- More robust [History](../features/history.md). Both [cmd](#cmd) and `cmd2` have readline history, +- More robust [History](../features/history.md). Both [cmd][cmd] and `cmd2` have readline history, but `cmd2` also has a robust `history` command which allows you to edit prior commands in a text - editor of your choosing, re-run multiple commands at a time, and save prior commands as a script - to be executed later. + editor of your choosing, re-run multiple commands at a time, save prior commands as a script to be + executed later, and much more. - Users can redirect output to a file or pipe it to some other operating system command. You did remember to use `self.stdout` instead of `sys.stdout` in all of your print functions, right? If you did, then this will work out of the box. If you didn't, you'll have to go back and fix them. - Before you do, you might consider the various ways `cmd2` has of + Before you do, you should consider the various ways `cmd2` has of [Generatoring Output](../features/generating_output.md). -- Users can load script files, which contain a series of commands to be executed. +- Users can load [script files](../features/scripting.md), which contain a series of commands to be + executed. - Users can create [Shortcuts, Aliases, and Macros](../features/shortcuts_aliases_macros.md) to reduce the typing required for repetitive commands. -- Embedded Python shell allows a user to execute Python code from within your `cmd2` app. How meta. +- [Embedded Python and/or IPython shells](../features/embedded_python_shells.md) allow a user to + execute Python code from within your `cmd2` app. How meta. - [Clipboard Integration](../features/clipboard.md) allows you to save command output to the operating system clipboard. - A built-in [Timer](../features/misc.md#Timer) can show how long it takes a command to execute @@ -53,3 +55,5 @@ In addition to the features you get with no additional work, `cmd2` offers a bro additional capabilities which can be easily added to your application. [Next Steps](next_steps.md) has some ideas of where you can start, or you can dig in to all the [Features](../features/index.md). + +[cmd]: https://docs.python.org/3/library/cmd diff --git a/docs/overview/alternatives.md b/docs/overview/alternatives.md index 35b21723a..1d7061d4b 100644 --- a/docs/overview/alternatives.md +++ b/docs/overview/alternatives.md @@ -9,9 +9,9 @@ UNIX-style options and flags). Though some people may prefer [argparse](https://docs.python.org/3/library/argparse.html). The [textual](https://textual.textualize.io/) module is capable of building sophisticated -full-screen terminal user interfaces that are not limited to simple text input and output; they can -paint the screen with options that are selected from using the cursor keys and even mouse clicks. -However, programming a `textual` application is not as straightforward as using `cmd2`. +full-screen terminal user interfaces (TUIs) that are not limited to simple text input and output; +they can paint the screen with options that are selected from using the cursor keys and even mouse +clicks. However, programming a `textual` application is not as straightforward as using `cmd2`. Several Python packages exist for building interactive command-line applications approximately similar in concept to [cmd](https://docs.python.org/3/library/cmd.html) applications. None of them @@ -36,6 +36,9 @@ Getting a working command-interpreter application based on either than `cmd2`. `cmd2` focuses on providing an excellent out-of-the-box experience with as many useful features as possible built in for free with as little work required on the developer's part as possible. We believe that `cmd2` provides developers the easiest way to write a command-line -interpreter, while allowing a good experience for end users. If you are seeking a visually richer -end-user experience and don't mind investing more development time, we would recommend checking out -[Python Prompt Toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit). +interpreter, while allowing a good experience for end users. + +If you are seeking a visually richer end-user experience and don't mind investing more development +time, we would recommend checking out [Textual](https://github.com/Textualize/textual) as this can +be used to build very sophisticated user interfaces in a terminal that are more akin to feature-rich +web GUIs. diff --git a/docs/overview/installation.md b/docs/overview/installation.md index 69f45630a..d9c2cc9d0 100644 --- a/docs/overview/installation.md +++ b/docs/overview/installation.md @@ -1,8 +1,8 @@ # Installation Instructions -`cmd2` works on Linux, macOS, and Windows. It requires Python 3.10 or higher, -[pip](https://pypi.org/project/pip), and [setuptools](https://pypi.org/project/setuptools). If -you've got all that, then you can just: +`cmd2` works on :simple-linux: Linux, :simple-apple: macOS, and :fontawesome-brands-windows: +Windows. It requires Python 3.10 or higher, [pip](https://pypi.org/project/pip), and +[setuptools](https://pypi.org/project/setuptools). If you've got all that, then you can just: ```shell $ pip install cmd2 @@ -16,6 +16,10 @@ $ pip install cmd2 $ sudo pip install ``` +!!! info + + You can also use an alternative Python package manager such as :simple-astral: [uv](https://github.com/astral-sh/uv), but doing so is beyond the scope of this installation guide. The `cmd2` developers love and highly recommend `uv` and use it for the development of `cmd2` itself. But chances are if you are a sophisticated enough Python developer to be using `uv`, you don't need us to tell you how to use it :smile: + ## Prerequisites If you have Python >=3.10 installed from [python.org](https://www.python.org), you will already have @@ -37,7 +41,7 @@ C:\> python -m pip install -U pip setuptools ## Install from PyPI {: #pip_install } [pip](https://pypi.org/project/pip) is the recommended installer. Installing packages from -[PyPI](https://pypi.org) with pip is easy: +:simple-pypi: [PyPI](https://pypi.org) with pip is easy: ```shell $ pip install cmd2 @@ -47,8 +51,8 @@ This will install the required 3rd-party dependencies, if necessary. ## Install from GitHub {: #github } -The latest version of `cmd2` can be installed directly from the main branch on GitHub using -[pip](https://pypi.org/project/pip): +The latest version of `cmd2` can be installed directly from the main branch on :simple-github: +GitHub using [pip](https://pypi.org/project/pip): ```shell $ pip install -U git+git://github.com/python-cmd2/cmd2.git @@ -57,7 +61,7 @@ $ pip install -U git+git://github.com/python-cmd2/cmd2.git ## Install from Debian or Ubuntu repos We recommend installing from [pip](https://pypi.org/project/pip), but if you wish to install from -Debian or Ubuntu repos this can be done with apt-get. +:simple-debian: Debian or :simple-ubuntu: Ubuntu repos this can be done with apt-get. For Python 3: @@ -67,7 +71,7 @@ This will also install the required 3rd-party dependencies. !!! warning - Versions of `cmd2` before 1.0.0 should be considered to be of unstable "beta" quality and should not be relied upon for production use. If you cannot get a version >= 0.8.9 from your OS repository, then we recommend installing from either pip or GitHub - see [Pip Install](installation.md#pip_install) or [Install from GitHub](installation.md#github). + Versions of `cmd2` before 2.0.0 should be considered to be of unstable "beta" quality and should not be relied upon for production use. If you cannot get a version >= 2.0.0 from your OS repository, then we recommend installing from either PyPI or GitHub - see [Pip Install](installation.md#pip_install) or [Install from GitHub](installation.md#github). ## Upgrading cmd2 @@ -87,17 +91,27 @@ If you wish to permanently uninstall `cmd2`, this can also easily be done with ## readline Considerations -Tab completion for `cmd2` applications is only tested against GNU Readline. It does not work -properly with the [libedit](http://thrysoee.dk/editline/) library which is similar, but not -identical to GNU Readline. `cmd2` will disable all tab-completion support if an incompatible version -of `readline` is found. +`cmd2` heavily relies on Python's built-in +[readline](https://docs.python.org/3/library/readline.html) module for its tab completion +capabilities. Tab completion for `cmd2` applications is only tested against :simple-gnu: +[GNU Readline](https://tiswww.case.edu/php/chet/readline/rltop.html) or libraries fully compatible +with it. It does not work properly with the :simple-netbsd: NetBSD +[Editline](http://thrysoee.dk/editline/) library (`libedit`) which is similar, but not identical to +GNU Readline. `cmd2` will disable all tab-completion support if an incompatible version of +`readline` is found. When installed using `pip`, `uv`, or similar Python packaging tool on either `macOS` or `Windows`, `cmd2` will automatically install a compatible version of readline. Most Linux operating systems come with a compatible version of readline. However, if you are using a tool like `uv` to install Python on your system and configure a virtual environment, `uv` installed -versions of Python come with `libedit`. +versions of Python come with `libedit`. If you are using `cmd2` on Linux with a version of Python +installed via `uv`, you will likely need to manually add the `gnureadline` Python module to your +`uv` virtual environment. + +```sh +uv pip install gnureadline +``` macOS comes with the [libedit](http://thrysoee.dk/editline/) library which is similar, but not identical, to GNU Readline. Tab completion for `cmd2` applications is only tested against GNU diff --git a/docs/overview/integrating.md b/docs/overview/integrating.md index b1db57534..3ac5ca3d4 100644 --- a/docs/overview/integrating.md +++ b/docs/overview/integrating.md @@ -3,11 +3,11 @@ Once installed, you will want to ensure that your project's dependencies include `cmd2`. Make sure your `pyproject.toml` or `setup.py` includes the following dependency - 'cmd2>=2.4' + 'cmd2>=2.7' -The `cmd2` project uses [Semantic Versioning](https://semver.org), which means that any incompatible -API changes will be release with a new major version number. The public API is documented in the -[API Reference](../api/index.md). +The `cmd2` project uses :simple-semver: [Semantic Versioning](https://semver.org), which means that +any incompatible API changes will be release with a new major version number. The public API is +documented in the [API Reference](../api/index.md). We recommend that you follow the advice given by the Python Packaging User Guide related to [install_requires](https://packaging.python.org/discussions/install-requires-vs-requirements/). By @@ -19,9 +19,10 @@ inadvertently get installed with an incompatible future version of `cmd2`. If you would like to use [Tab Completion](../features/completion.md), then you need a compatible version of [readline](https://tiswww.case.edu/php/chet/readline/rltop.html) installed on your operating system (OS). `cmd2` forces a sane install of `readline` on both `Windows` and `macOS`, but -does not do so on `Linux`. If for some reason, you have a Linux OS that has the -[Editline Library (libedit)](https://www.thrysoee.dk/editline/) installed instead of `readline`, you -will need to manually add a dependency on `gnureadline`. Make sure to include the following -dependency in your `pyproject.toml` or `setup.py`: +does not do so on `Linux`. If for some reason, you have a version of Python on a Linux OS who's +built-in `readline` module is based on the +[Editline Library (libedit)](https://www.thrysoee.dk/editline/) instead of `readline`, you will need +to manually add a dependency on `gnureadline`. Make sure to include the following dependency in your +`pyproject.toml` or `setup.py`: 'gnureadline' diff --git a/docs/overview/resources.md b/docs/overview/resources.md index f781f4627..cd5accc7b 100644 --- a/docs/overview/resources.md +++ b/docs/overview/resources.md @@ -5,7 +5,3 @@ Project related links and other resources: - [cmd](https://docs.python.org/3/library/cmd.html) - [cmd2 project page](https://github.com/python-cmd2/cmd2) - [project bug tracker](https://github.com/python-cmd2/cmd2/issues) -- PyOhio 2019: - [slides](https://github.com/python-cmd2/talks/blob/main/PyOhio_2019/cmd2-PyOhio_2019.pdf), - [video](https://www.youtube.com/watch?v=pebeWrTqIIw), - [examples](https://github.com/python-cmd2/talks/tree/main/PyOhio_2019/examples) From 6b84bd4fe0d560f4445f655337c14691dcacfe7d Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 18:39:06 -0400 Subject: [PATCH 02/18] More documentation updates --- docs/features/builtin_commands.md | 2 +- docs/features/completion.md | 22 +++++----- docs/features/disable_commands.md | 24 +++++++---- docs/features/embedded_python_shells.md | 14 ++++--- docs/features/generating_output.md | 56 ++++++++++++++----------- examples/remove_builtin_commands.py | 2 +- 6 files changed, 69 insertions(+), 51 deletions(-) diff --git a/docs/features/builtin_commands.md b/docs/features/builtin_commands.md index 7deed08ba..5cefb35a1 100644 --- a/docs/features/builtin_commands.md +++ b/docs/features/builtin_commands.md @@ -35,7 +35,7 @@ history. See [History](history.md) for more information. ### ipy (optional) -This optional opt-in command enters an interactive IPython shell. See +This optional opt-in command enters an interactive :simple-jupyter: IPython shell. See [IPython (optional)](./embedded_python_shells.md#ipython-optional) for more information. ### macro diff --git a/docs/features/completion.md b/docs/features/completion.md index e56056131..014dcb810 100644 --- a/docs/features/completion.md +++ b/docs/features/completion.md @@ -78,8 +78,8 @@ user. These include the following example cases: - A previous command line argument that determines the data set being completed is invalid - Tab completion hints -`cmd2` provides the `cmd2.exceptions.CompletionError` exception class for this capability. If an -error occurs in which it is more desirable to display a message than a stack trace, then raise a +`cmd2` provides the [CompletionError][cmd2.CompletionError] exception class for this capability. If +an error occurs in which it is more desirable to display a message than a stack trace, then raise a `CompletionError`. By default, the message displays in red like an error. However, `CompletionError` has a member called `apply_style`. Set this False if the error style should not be applied. For instance, `ArgparseCompleter` sets it to False when displaying completion hints. @@ -90,7 +90,7 @@ When using `cmd2`'s [@with_argparser][cmd2.with_argparser] decorator, `cmd2` pro completion of flag names. Tab completion of argument values can be configured by using one of three parameters to -`argparse.ArgumentParser.add_argument` +[argparse.ArgumentParser.add_argument](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument) - `choices` - `choices_provider` @@ -105,17 +105,17 @@ example for a demonstration of how to use the `choices_provider` parameter. See [argparse_completion](https://github.com/python-cmd2/cmd2/blob/main/examples/argparse_completion.py) example for a demonstration of how to use the `completer` parameter. -When tab completing flags or argument values for a `cmd2` command using one of these decorators, -`cmd2` keeps track of state so that once a flag has already previously been provided, it won't -attempt to tab complete it again. When no completion results exist, a hint for the current argument -will be displayed to help the user. +When tab completing flags or argument values for a `cmd2` command using the `@with_argparser` +decorator, `cmd2` keeps track of state so that once a flag has already previously been provided, it +won't attempt to tab complete it again. When no completion results exist, a hint for the current +argument will be displayed to help the user. ## CompletionItem For Providing Extra Context When tab completing things like a unique ID from a database, it can often be beneficial to provide the user with some extra context about the item being completed, such as a description. To -facilitate this, `cmd2` defines the `cmd2.CompletionItem` class which can be returned from any of -the 3 completion parameters: `choices`, `choices_provider`, and `completer`. +facilitate this, `cmd2` defines the [CompletionItem][cmd2.CompletionItem] class which can be +returned from any of the 3 completion parameters: `choices`, `choices_provider`, and `completer`. See the [argparse_completion](https://github.com/python-cmd2/cmd2/blob/main/examples/argparse_completion.py) @@ -124,8 +124,8 @@ demonstration of how this is used. ## Custom Completion with `read_input()` -`cmd2` provides `cmd2.Cmd.read_input` as an alternative to Python's `input()` function. `read_input` -supports configurable tab completion and up-arrow history at the prompt. See +`cmd2` provides [cmd2.Cmd.read_input][] as an alternative to Python's `input()` function. +`read_input` supports configurable tab completion and up-arrow history at the prompt. See [read_input](https://github.com/python-cmd2/cmd2/blob/main/examples/read_input.py) example for a demonstration. diff --git a/docs/features/disable_commands.md b/docs/features/disable_commands.md index 3dcb0255d..d934de497 100644 --- a/docs/features/disable_commands.md +++ b/docs/features/disable_commands.md @@ -2,14 +2,21 @@ `cmd2` allows a developer to: -- remove commands included in `cmd2` -- prevent commands from appearing in the help menu (hide commands) -- disable and re-enable commands at runtime +- Remove commands included in `cmd2` +- Prevent commands from appearing in the help menu (hide commands) +- Disable and re-enable commands at runtime + +See +[remove_builtin_commands.py](https://github.com/python-cmd2/cmd2/blob/main/examples/remove_builtin_commands.py) +for and example of removing or hiding built-in commands. + +See [command_sets.py](https://github.com/python-cmd2/cmd2/blob/main/examples/command_sets.py) for an +example of dynamically enabling and dis-abling custom commands at runtime. ## Remove A Command When a command has been removed, the command method has been deleted from the object. The command -doesn't show up in help, and it can't be executed. This approach is appropriate if you never want a +doesn't show up in help and it can't be executed. This approach is appropriate if you never want a built-in command to be part of your application. Delete the command method in your initialization code: @@ -26,9 +33,9 @@ code: ## Hide A Command -When a command is hidden, it won't show up in the help menu, but if the user knows it's there and -types the command, it will be executed. You hide a command by adding it to the `hidden_commands` -list: +When a command is hidden, it won't show up in the help menu and it won't tab-complete, but if the +user knows it's there and types the command, it will be executed. You hide a command by adding it to +the `hidden_commands` list: ```py class HiddenCommands(cmd2.Cmd): @@ -101,3 +108,6 @@ Similarly, you can re-enable all the commands in a category: ```py self.enable_category('Server Information') ``` + +See [help_categories.py](https://github.com/python-cmd2/cmd2/blob/main/examples/help_categories.py) +for an example of enabling and disabling and entire category of commands dynamically at runtime. diff --git a/docs/features/embedded_python_shells.md b/docs/features/embedded_python_shells.md index bf5bf594c..0a4b60f42 100644 --- a/docs/features/embedded_python_shells.md +++ b/docs/features/embedded_python_shells.md @@ -2,8 +2,8 @@ ## Python (optional) -If the `cmd2.Cmd` class is instantiated with `include_py=True`, then the optional `py` command will -be present and run an interactive Python shell: +If the [cmd2.Cmd][] class is instantiated with `include_py=True`, then the optional `py` command +will be present and run an interactive Python shell: ```py from cmd2 import Cmd @@ -33,10 +33,12 @@ All of these parameters are also available to Python scripts which run in your a - has the ability to pass command-line arguments to the scripts invoked This command provides a more complex and powerful scripting capability than that provided by the -simple text file scripts. Python scripts can include conditional control flow logic. See the -**python_scripting.py** `cmd2` application and the **script_conditional.py** script in the -`examples` source code directory for an example of how to achieve this in your own applications. See -[Scripting](./scripting.md) for an explanation of both scripting methods in **cmd2** applications. +simple text file scripts. Python scripts can include conditional control flow logic. See +[python_scripting.py](https://github.com/python-cmd2/cmd2/blob/main/examples/python_scripting.py) +`cmd2` and the +[conditional.py](https://github.com/python-cmd2/cmd2/blob/main/examples/scripts/conditional.py) +script for an example of how to achieve this in your own applications. See +[Scripting](./scripting.md) for an explanation of both scripting methods in `cmd2` applications. A simple example of using `run_pyscript` is shown below along with the [arg_printer](https://github.com/python-cmd2/cmd2/blob/main/examples/scripts/arg_printer.py) script: diff --git a/docs/features/generating_output.md b/docs/features/generating_output.md index b9356bfe0..db2c29b67 100644 --- a/docs/features/generating_output.md +++ b/docs/features/generating_output.md @@ -26,9 +26,9 @@ complex output. ## Ordinary Output -The `cmd2.Cmd.poutput` method is similar to the Python -[built-in print function](https://docs.python.org/3/library/functions.html#print). -`cmd2.Cmd.poutput` adds two conveniences: +The [poutput][cmd2.Cmd.poutput] method is similar to the Python built-in +[print](https://docs.python.org/3/library/functions.html#print) function. `poutput` adds a few +conveniences: 1. Since users can pipe output to a shell command, it catches `BrokenPipeError` and outputs the contents of `self.broken_pipe_warning` to `stderr`. `self.broken_pipe_warning` defaults to an @@ -36,6 +36,7 @@ The `cmd2.Cmd.poutput` method is similar to the Python message, put it in `self.broken_pipe_warning` when you initialize `cmd2.Cmd`. 2. It examines and honors the [allow_style](./settings.md#allow_style) setting. See [Colored Output](#colored-output) below for more details. +3. It allows printing arbitrary `rich` renderable objects which can get visually quite complex. Here's a simple command that shows this method in action: @@ -48,11 +49,13 @@ def do_echo(self, args): ## Error Messages When an error occurs in your program, you can display it on `sys.stderr` by calling the -`.cmd2.Cmd.perror` method. By default this method applies `Cmd2Style.ERROR` to the output. +[perror][cmd2.Cmd.perror] method. By default this method applies +[Cmd2Style.ERROR][cmd2.Cmd2Style.ERROR] to the output. ## Warning Messages -`cmd2.Cmd.pwarning` is just like `cmd2.Cmd.perror` but applies `Cmd2Style.WARNING` to the output. +[pwarning][cmd2.Cmd.pwarning] is just like `cmd2.Cmd.perror` but applies `Cmd2Style.WARNING` to the +output. ## Feedback @@ -60,8 +63,8 @@ You may have the need to display information to the user which is not intended t generated output. This could be debugging information or status information about the progress of long running commands. It's not output, it's not error messages, it's feedback. If you use the [Timing](./settings.md#timing) setting, the output of how long it took the command to run will be -output as feedback. You can use the `cmd2.Cmd.pfeedback` method to produce this type of output, and -several [Settings](./settings.md) control how it is handled. +output as feedback. You can use the [pfeedback][cmd2.Cmd.pfeedback] method to produce this type of +output, and several [Settings](./settings.md) control how it is handled. If the [quiet](./settings.md#quiet) setting is `True`, then calling `cmd2.Cmd.pfeedback` produces no output. If [quiet](./settings.md#quiet) is `False`, the @@ -71,17 +74,19 @@ send the output to `stdout` or `stderr`. ## Exceptions If your app catches an exception and you would like to display the exception to the user, the -`cmd2.Cmd.pexcept` method can help. The default behavior is to just display the message contained -within the exception. However, if the [debug](./settings.md#debug) setting is `True`, then the -entire stack trace will be displayed. +[pexcept][cmd2.Cmd.pexcept] method can help. The default behavior is to just display the message +contained within the exception. However, if the [debug](./settings.md#debug) setting is `True`, then +the entire stack trace will be displayed. ## Paging Output If you know you are going to generate a lot of output, you may want to display it in a way that the user can scroll forwards and backwards through it. If you pass all of the output to be displayed in -a single call to `.cmd2.Cmd.ppaged`, it will be piped to an operating system appropriate shell -command to page the output. On Windows, the output is piped to `more`; on Unix-like operating -systems like MacOS and Linux, it is piped to `less`. +a single call to [ppaged][cmd2.Cmd.ppaged], it will be piped to an operating system appropriate +shell command to page the output. On Windows, the output is piped to +[more](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/more); on +Unix-like operating systems like MacOS and Linux, it is piped to +[less](https://man7.org/linux/man-pages/man1/less.1.html). ## Colored Output @@ -102,20 +107,21 @@ all colors available to your `cmd2` application. ### Custom Themes -`cmd2` uses a `rich` `Theme` object to define styles for various UI elements. You can define your -own custom theme using `cmd2.rich_utils.set_theme`. See the +`cmd2` uses a `rich` [Theme](https://rich.readthedocs.io/en/stable/reference/theme.html) object to +define styles for various UI elements. You can define your own custom theme using +[cmd2.rich_utils.set_theme][]. See the [rich_theme.py](https://github.com/python-cmd2/cmd2/blob/main/examples/rich_theme.py) example for more information. After adding the desired escape sequences to your output, you should use one of these methods to present the output to the user: -- `cmd2.Cmd.poutput` -- `cmd2.Cmd.perror` -- `cmd2.Cmd.pwarning` -- `cmd2.Cmd.pexcept` -- `cmd2.Cmd.pfeedback` -- `cmd2.Cmd.ppaged` +- [cmd2.Cmd.poutput][] +- [cmd2.Cmd.perror][] +- [cmd2.Cmd.pwarning][] +- [cmd2.Cmd.pexcept][] +- [cmd2.Cmd.pfeedback][] +- [cmd2.Cmd.ppaged][] These methods all honor the [allow_style](./settings.md#allow_style) setting, which users can modify to control whether these escape codes are passed through to the terminal or not. @@ -125,9 +131,9 @@ to control whether these escape codes are passed through to the terminal or not. If you would like to generate output which is left, center, or right aligned within a specified width or the terminal width, the following functions can help: -- `cmd2.string_utils.align_left` -- `cmd2.string_utils.align_center` -- `cmd2.string_utils.align_right` +- [cmd2.string_utils.align_left][] +- [cmd2.string_utils.align_center][] +- [cmd2.string_utils.align_right][] These functions differ from Python's string justifying functions in that they support characters with display widths greater than 1. Additionally, ANSI style sequences are safely ignored and do not @@ -141,6 +147,6 @@ you can pad it appropriately with spaces. However, there are categories of Unico occupy 2 cells, and other that occupy 0. To further complicate matters, you might have included ANSI escape sequences in the output to generate colors on the terminal. -The `cmd2.string_utils.str_width` function solves both of these problems. Pass it a string, and +The [cmd2.string_utils.str_width][] function solves both of these problems. Pass it a string, and regardless of which Unicode characters and ANSI text style escape sequences it contains, it will tell you how many characters on the screen that string will consume when printed. diff --git a/examples/remove_builtin_commands.py b/examples/remove_builtin_commands.py index 64acd17d8..eb226c7a8 100755 --- a/examples/remove_builtin_commands.py +++ b/examples/remove_builtin_commands.py @@ -18,7 +18,7 @@ def __init__(self) -> None: super().__init__() # To hide commands from displaying in the help menu, add them to the hidden_commands list - self.hidden_commands.append('py') + self.hidden_commands.append('history') # To remove built-in commands entirely, delete their "do_*" function from the cmd2.Cmd class del cmd2.Cmd.do_edit From ef30d8dc027709e815ffb172d83e0e83ca69c456 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 18:50:18 -0400 Subject: [PATCH 03/18] Updated docs/features/help.md --- docs/features/help.md | 130 +++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/docs/features/help.md b/docs/features/help.md index 437546f07..6def1f5b1 100644 --- a/docs/features/help.md +++ b/docs/features/help.md @@ -12,8 +12,8 @@ command. The `help` command by itself displays a list of the commands available: ```text (Cmd) help -Documented commands (use 'help -v' for verbose/'help ' for details): -=========================================================================== +Documented Commands +─────────────────── alias help ipy py run_pyscript set shortcuts edit history macro quit run_script shell ``` @@ -24,9 +24,9 @@ The `help` command can also be used to provide detailed help for a specific comm (Cmd) help quit Usage: quit [-h] -Exit this application +Exit this application. -optional arguments: +Optional Arguments: -h, --help show this help message and exit ``` @@ -37,8 +37,9 @@ help for a command is the docstring for the `do_*` method defining the command - **foo**, that command is implemented by defining the `do_foo` method and the docstring for that method is the help. -For commands which use one of the `argparse` decorators to parse arguments, help is provided by -`argparse`. See [Help Messages](./argument_processing.md#help-messages) for more information. +For commands which use the [@with_argparser][cmd2.with_argparser] decorator to parse arguments, help +is provided by `argparse`. See [Help Messages](./argument_processing.md#help-messages) for more +information. Occasionally there might be an unusual circumstance where providing static help text isn't good enough and you want to provide dynamic information in the help text for a command. To meet this @@ -51,42 +52,41 @@ not use an `argparse` decorator because we didn't want different output for `hel By default, the `help` command displays: - Documented commands (use 'help -v' for verbose/'help ' for details): - =========================================================================== + Documented Commands + ─────────────────── alias help ipy py run_pyscript set shortcuts edit history macro quit run_script shell If you have a large number of commands, you can optionally group your commands into categories. -Here's the output from the example `help_categories.py`: - - Documented commands (use 'help -v' for verbose/'help ' for details): +Here's the output from the example +[help_categories.py](https://github.com/python-cmd2/cmd2/blob/main/examples/help_categories.py): Application Management - ====================== + ────────────────────── deploy findleakers redeploy sessions stop expire list restart start undeploy Command Management - ================== + ────────────────── disable_commands enable_commands Connecting - ========== + ────────── connect which Server Information - ================== + ────────────────── resources serverinfo sslconnectorciphers status thread_dump vminfo Other - ===== - alias edit history py run_pyscript set shortcuts - config help macro quit run_script shell version + ───── + alias edit history quit run_script shell version + config help macro run_pyscript set shortcuts -There are 2 methods of specifying command categories, using the `@with_category` decorator or with -the `categorize()` function. Once a single command category is detected, the help output switches to -a categorized mode of display. All commands without an explicit category defined default to the -category `Other`. +There are 2 methods of specifying command categories, using the [@with_category][cmd2.with_category] +decorator or with the [categorize()][cmd2.categorize] function. Once a single command category is +detected, the help output switches to a categorized mode of display. All commands without an +explicit category defined default to the category `Other`. Using the `@with_category` decorator: @@ -134,58 +134,70 @@ categorize((do_undeploy, The `help` command also has a verbose option (`help -v` or `help --verbose`) that combines the help categories with per-command help messages: - Documented commands (use 'help -v' for verbose/'help ' for details): - Application Management - ====================================================================================================== - deploy Deploy command. - expire Expire command. - findleakers Find Leakers command. - list List command. - redeploy Redeploy command. - restart Restart command. - sessions Sessions command. - start Start command. - stop Stop command. - undeploy Undeploy command. + ───────────────────────────────────── + Name Description + ───────────────────────────────────── + deploy Deploy command. + expire Expire command. + findleakers Find Leakers command. + list List command. + redeploy Redeploy command. + restart Restart command. + sessions Sessions command. + start Start command. + stop Stop command. + undeploy Undeploy command. + Command Management - ====================================================================================================== - disable_commands Disable the Application Management commands. - enable_commands Enable the Application Management commands. + ───────────────────────────────────────────────────────────────── + Name Description + ───────────────────────────────────────────────────────────────── + disable_commands Disable the Application Management commands. + enable_commands Enable the Application Management commands. + Connecting - ====================================================================================================== - connect Connect command. - which Which command. + ──────────────────────────── + Name Description + ──────────────────────────── + connect Connect command. + which Which command. + Server Information - ====================================================================================================== + ───────────────────────────────────────────────────────────────────────────────────────────────── + Name Description + ───────────────────────────────────────────────────────────────────────────────────────────────── resources Resources command. serverinfo Server Info command. sslconnectorciphers SSL Connector Ciphers command is an example of a command that contains - multiple lines of help information for the user. Each line of help in a - contiguous set of lines will be printed and aligned in the verbose output - provided with 'help --verbose'. + multiple lines of help information for the user. Each line of help in a + contiguous set of lines will be printed and aligned in the verbose output + provided with 'help --verbose'. status Status command. thread_dump Thread Dump command. vminfo VM Info command. + Other - ====================================================================================================== - alias Manage aliases. - config Config command. - edit Run a text editor and optionally open a file with it. - help List available commands or provide detailed help for a specific command. - history View, run, edit, save, or clear previously entered commands. - macro Manage macros. - quit Exit this application. - run_pyscript Run Python script within this application's environment. - run_script Run text script. - set Set a settable parameter or show current settings of parameters. - shell Execute a command as if at the OS prompt. - shortcuts List available shortcuts. - version Version command. + ───────────────────────────────────────────────────────────────────────────────────────── + Name Description + ───────────────────────────────────────────────────────────────────────────────────────── + alias Manage aliases. + config Config command. + edit Run a text editor and optionally open a file with it. + help List available commands or provide detailed help for a specific command. + history View, run, edit, save, or clear previously entered commands. + macro Manage macros. + quit Exit this application. + run_pyscript Run Python script within this application's environment. + run_script Run text script. + set Set a settable parameter or show current settings of parameters. + shell Execute a command as if at the OS prompt. + shortcuts List available shortcuts. + version Version command. When called with the `-v` flag for verbose help, the one-line description for each command is provided by the first line of the docstring for that command's associated `do_*` method. From 6cbc0b4a35f1d708e19805554ad046263766c7eb Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 20:11:55 -0400 Subject: [PATCH 04/18] Continuing to update documentation --- docs/features/history.md | 21 ++++---- docs/features/hooks.md | 59 ++++++++++++----------- docs/features/initialization.md | 2 +- docs/features/misc.md | 23 ++++----- docs/features/modular_commands.md | 80 ++++++++++++++++--------------- 5 files changed, 95 insertions(+), 90 deletions(-) diff --git a/docs/features/history.md b/docs/features/history.md index 9dfe9f51a..5760bfd57 100644 --- a/docs/features/history.md +++ b/docs/features/history.md @@ -14,9 +14,9 @@ Each time a command is executed (this gets complex, see [cmd2.Statement][] is appended to `cmd2.Cmd.history`. `cmd2` adds the option of making this history persistent via optional arguments to -`cmd2.Cmd.__init__`. If you pass a filename in the `persistent_history_file` argument, the contents -of `cmd2.Cmd.history` will be written as compressed JSON to that history file. We chose this format -instead of plain text to preserve the complete `cmd2.Statement` object for each command. +[cmd2.Cmd.\_\_init\_\_][]. If you pass a filename in the `persistent_history_file` argument, the +contents of `cmd2.Cmd.history` will be written as compressed JSON to that history file. We chose +this format instead of plain text to preserve the complete `cmd2.Statement` object for each command. !!! note @@ -25,21 +25,22 @@ instead of plain text to preserve the complete `cmd2.Statement` object for each However, this design choice causes an inconsistency between the `readline` history and the `cmd2` history when you enter an invalid command: it is saved to the `readline` history, but not to the `cmd2` history. The `cmd2.Cmd.history` attribute, the `cmd2.history.History` class, and the -`cmd2.history.HistoryItem` class are all part of the public API for `cmd2.Cmd`. You could use these -classes to implement write your own `history` command (see below for documentation on how the -included `history` command works). +[cmd2.history.HistoryItem][] class are all part of the public API for `cmd2.Cmd`. You could use +these classes to implement your own `history` command (see below for documentation on how the +built-in `history` command works). ## For Users -You can use the up and down arrow keys to move through the history of previously entered commands. +You can use the :arrow_up: up and :arrow_down: down arrow keys to move through the history of +previously entered commands. If the `readline` module is installed, you can press `Control-p` to move to the previously entered command, and `Control-n` to move to the next command. You can also search through the command history using `Control-r`. -Eric Johnson hosts a nice [readline cheat sheet](http://readline.kablamo.org/emacs.html), or you can -dig into the [GNU Readline User Manual](http://man7.org/linux/man-pages/man3/readline.3.html) for -all the details, including instructions for customizing the key bindings. +You can refer to the [readline cheat sheet](http://readline.kablamo.org/emacs.html) or you can dig +into the [GNU Readline User Manual](http://man7.org/linux/man-pages/man3/readline.3.html) for all +the details, including instructions for customizing the key bindings. `cmd2` makes a third type of history access available with the `history` command. Each time the user enters a command, `cmd2` saves the input. The `history` command lets you do interesting things with diff --git a/docs/features/hooks.md b/docs/features/hooks.md index 65cbce4d7..2b877ccf4 100644 --- a/docs/features/hooks.md +++ b/docs/features/hooks.md @@ -54,22 +54,23 @@ called, the `cmd2.Cmd.postloop` method is called. Preloop and postloop hook methods are not passed any parameters and any return value is ignored. The approach of registering hooks instead of overriding methods allows multiple hooks to be called -before the command loop begins or ends. Plugin authors should review [Hooks](./hooks.md) for best -practices writing hooks. +before the command loop begins or ends. Plugin authors should review this page carefully in full for +best practices writing hooks. ## Application Lifecycle Attributes -There are numerous attributes on `cmd2.Cmd` which affect application behavior upon entering or +There are numerous attributes on [cmd2.Cmd][] which affect application behavior upon entering or during the command loop: -- `cmd2.Cmd.intro` - if provided this serves as the intro banner printed once at start of +- [cmd2.Cmd.intro][] - if provided this serves as the intro banner printed once at start of application, after `cmd2.Cmd.preloop` is called -- `cmd2.Cmd.prompt` - see [Prompt](./prompt.md) for more information -- `cmd2.Cmd.continuation_prompt` - The prompt issued to solicit input for the 2nd and subsequent +- [cmd2.Cmd.prompt][] - see [Prompt](./prompt.md) for more information +- [cmd2.Cmd.continuation_prompt][] - The prompt issued to solicit input for the 2nd and subsequent lines of a [Multiline Command](./multiline_commands.md) -- `cmd2.Cmd.echo` - if `True` write the prompt and the command into the output stream +- [cmd2.Cmd.echo][] - if `True` write the prompt and the command into the output stream -In addition, several arguments to `cmd2.Cmd.__init__` also affect the command loop behavior: +In addition, several arguments to [cmd2.Cmd.\_\_init\_\_][cmd2.Cmd.__init__] also affect the command +loop behavior: - `allow_cli_args` - allows commands to be specified on the operating system command line which are executed before the command processing loop begins @@ -84,25 +85,25 @@ application exits: 1. Output the prompt 1. Accept user input -1. Parse user input into a `cmd2.Statement` object -1. Call methods registered with `cmd2.Cmd.register_postparsing_hook()` +1. Parse user input into a [cmd2.Statement][] object +1. Call methods registered with [cmd2.Cmd.register_postparsing_hook][] 1. Redirect output, if user asked for it and it's allowed 1. Start timer -1. Call methods registered with `cmd2.Cmd.register_precmd_hook` -1. Call `cmd2.Cmd.precmd` - for backwards compatibility with `cmd.Cmd` +1. Call methods registered with [cmd2.Cmd.register_precmd_hook][] +1. Call [cmd2.Cmd.precmd][] - for backwards compatibility with `cmd.Cmd` 1. Add statement to [History](./history.md) 1. Call `do_command` method -1. Call methods registered with `cmd2.Cmd.register_postcmd_hook()` -1. Call `cmd2.Cmd.postcmd` - for backwards compatibility with `cmd.Cmd` +1. Call methods registered with [cmd2.Cmd.register_postcmd_hook][] +1. Call [cmd2.Cmd.postcmd][] - for backwards compatibility with `cmd.Cmd` 1. Stop timer and display the elapsed time 1. Stop redirecting output if it was redirected -1. Call methods registered with `cmd2.Cmd.register_cmdfinalization_hook()` +1. Call methods registered with [cmd2.Cmd.register_cmdfinalization_hook][] By registering hook methods, multiple steps allow you to run code during, and control the flow of the command processing loop. Be aware that plugins also utilize these hooks, so there may be code -running that is not part of your application. Methods registered for a hook are called in the order -they were registered. You can register a function more than once, and it will be called each time it -was registered. +running that is not directly part of your application code. Methods registered for a hook are called +in the order they were registered. You can register a function more than once, and it will be called +each time it was registered. Postparsing, precommand, and postcommand hook methods share some common ways to influence the command processing loop. @@ -146,7 +147,7 @@ class App(cmd2.Cmd): a `TypeError` if it has the wrong number of parameters. It will also raise a `TypeError` if the passed parameter and return value are not annotated as `PostparsingData`. -The hook method will be passed one parameter, a `cmd2.plugin.PostparsingData` object which we will +The hook method will be passed one parameter, a [cmd2.plugin.PostparsingData][] object which we will refer to as `params`. `params` contains two attributes. `params.statement` is a `cmd2.Statement` object which describes the parsed user input. There are many useful attributes in the `cmd2.Statement` object, including `.raw` which contains exactly what the user typed. `params.stop` @@ -162,10 +163,10 @@ To modify the user input, you create a new `cmd2.Statement` object and return it be dragons. Instead, use the various attributes in a `cmd2.Statement` object to construct a new string, and then parse that string to create a new `cmd2.Statement` object. -`cmd2.Cmd` uses an instance of `cmd2.parsing.StatementParser` to parse user input. This instance has -been configured with the proper command terminators, multiline commands, and other parsing related -settings. This instance is available as the `cmd2.Cmd.statement_parser` attribute. Here's a simple -example which shows the proper technique: +`cmd2.Cmd` uses an instance of [cmd2.parsing.StatementParser][] to parse user input. This instance +has been configured with the proper command terminators, multiline commands, and other parsing +related settings. This instance is available as the `cmd2.Cmd.statement_parser` attribute. Here's a +simple example which shows the proper technique: ```py def myhookmethod(self, params: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: @@ -211,8 +212,8 @@ parameters and return value are not annotated as `PrecommandData`. You may choose to modify the user input by creating a new `cmd2.Statement` with different properties (see above). If you do so, assign your new `cmd2.Statement` object to `data.statement`. -The precommand hook must return a `cmd2.plugin.PrecommandData` object. You don't have to create this -object from scratch, you can just return the one passed into the hook. +The precommand hook must return a [cmd2.plugin.PrecommandData][] object. You don't have to create +this object from scratch, you can just return the one passed into the hook. After all registered precommand hooks have been called, `cmd2.Cmd.precmd` will be called. To retain full backward compatibility with `cmd.Cmd`, this method is passed a `cmd2.Statement`, not a @@ -236,7 +237,7 @@ class App(cmd2.Cmd): return data ``` -Your hook will be passed a `cmd2.plugin.PostcommandData` object, which has a +Your hook will be passed a [cmd2.plugin.PostcommandData][] object, which has a `cmd2.plugin.PostcommandData.statement` attribute that describes the command which was executed. If your postcommand hook method gets called, you are guaranteed that the command method was called, and that it didn't raise an exception. @@ -263,8 +264,8 @@ otherwise. To purposefully and silently skip postcommand hooks, commands can raise any of the following exceptions. -- `cmd2.exceptions.SkipPostcommandHooks` -- `cmd2.exceptions.Cmd2ArgparseError` +- [cmd2.exceptions.SkipPostcommandHooks][] +- [cmd2.exceptions.Cmd2ArgparseError][] ## Command Finalization Hooks @@ -282,7 +283,7 @@ class App(cmd2.Cmd): ``` Command Finalization hooks must check whether the `cmd2.plugin.CommandFinalizationData.statement` -attribute of the passed `cmd2.plugin.CommandFinalizationData` object contains a value. There are +attribute of the passed [cmd2.plugin.CommandFinalizationData][] object contains a value. There are certain circumstances where these hooks may be called before the user input has been parsed, so you can't always rely on having a `cmd2.plugin.CommandFinalizationData.statement`. diff --git a/docs/features/initialization.md b/docs/features/initialization.md index 5fe6e008d..1ffb21677 100644 --- a/docs/features/initialization.md +++ b/docs/features/initialization.md @@ -12,7 +12,7 @@ Here is a basic example `cmd2` application which demonstrates many capabilities ## Cmd class initializer -A `cmd2.Cmd` instance or subclass instance is an interactive CLI application framework. There is no good reason to instantiate `Cmd` itself; rather, it's useful as a superclass of a class you define yourself in order to inherit `Cmd`'s methods and encapsulate action methods. +A [cmd2.Cmd][] instance or subclass instance is an interactive CLI application framework. There is no good reason to instantiate `Cmd` itself; rather, it's useful as a superclass of a class you define yourself in order to inherit `Cmd`'s methods and encapsulate action methods. Certain things must be initialized within the `__init__()` method of your class derived from `cmd2.Cmd`(all arguments to `__init__()` are optional): diff --git a/docs/features/misc.md b/docs/features/misc.md index 1bb357c6a..1915b3302 100644 --- a/docs/features/misc.md +++ b/docs/features/misc.md @@ -41,18 +41,19 @@ available when the application is in a specific state. When a command is disable up in the help menu or tab complete. If a user tries to run the command, a command-specific message supplied by the developer will be printed. The following functions support this feature. -- **enable_command** : Enable an individual command -- **enable_category** : Enable an entire category of commands -- **disable_command** : Disable an individual command and set the message that will print when this - command is run or help is called on it while disabled -- **disable_category** : Disable an entire category of commands and set the message that will print - when anything in this category is run or help is called on it while disabled +- [enable_command][cmd2.Cmd.enable_command] : Enable an individual command +- [enable_category][cmd2.Cmd.enable_category] : Enable an entire category of commands +- [disable_command][cmd2.Cmd.disable_command] : Disable an individual command and set the message + that will print when this command is run or help is called on it while disabled +- [disable_category][cmd2.Cmd.disable_category] : Disable an entire category of commands and set the + message that will print when anything in this category is run or help is called on it while + disabled See the definitions of these functions for descriptions of their arguments. See the `do_enable_commands()` and `do_disable_commands()` functions in the -[HelpCategories](https://github.com/python-cmd2/cmd2/blob/main/examples/help_categories.py) example -for a demonstration. +[help_categories.py](https://github.com/python-cmd2/cmd2/blob/main/examples/help_categories.py) +example for a demonstration. ## Default to shell @@ -64,9 +65,9 @@ shortcut: (Cmd) !which python /usr/bin/python -However, if the parameter `default_to_shell` is `True`, then _every_ command will be attempted on -the operating system. Only if that attempt fails (i.e., produces a nonzero return value) will the -application's own `default` method be called. +However, if the parameter `default_to_shell` is `True`, then _every_ thing entered which doesn't +match another command will be attempted on the operating system. Only if that attempt fails (i.e., +produces a nonzero return value) will the application's own `default` method be called. (Cmd) which python /usr/bin/python diff --git a/docs/features/modular_commands.md b/docs/features/modular_commands.md index bc33d7dd0..3bb2c64d5 100644 --- a/docs/features/modular_commands.md +++ b/docs/features/modular_commands.md @@ -2,20 +2,20 @@ ## Overview -Cmd2 also enables developers to modularize their command definitions into `CommandSet` objects. -CommandSets represent a logical grouping of commands within an cmd2 application. By default, all -CommandSets will be discovered and loaded automatically when the `cmd2.Cmd` class is instantiated -with this mixin. This also enables the developer to dynamically add/remove commands from the cmd2 -application. This could be useful for loadable plugins that add additional capabilities. -Additionally, it allows for object-oriented encapsulation and garbage collection of state that is -specific to a CommandSet. +Cmd2 also enables developers to modularize their command definitions into +[CommandSet][cmd2.CommandSet] objects. CommandSets represent a logical grouping of commands within a +`cmd2` application. By default, all `CommandSet` objects will be discovered and loaded automatically +when the [cmd2.Cmd][] class is instantiated with this mixin. This also enables the developer to +dynamically add/remove commands from the cmd2 application. This could be useful for loadable plugins +that add additional capabilities. Additionally, it allows for object-oriented encapsulation and +garbage collection of state that is specific to a CommandSet. ### Features - Modular Command Sets - Commands can be broken into separate modules rather than in one god class holding all commands. - Automatic Command Discovery - In your application, merely defining and importing a CommandSet is - sufficient for cmd2 to discover and load your command. No manual registration is necessary. + sufficient for `cmd2` to discover and load your command. No manual registration is necessary. - Dynamically Loadable/Unloadable Commands - Command functions and CommandSets can both be loaded and unloaded dynamically during application execution. This can enable features such as dynamically loaded modules that add additional commands. @@ -25,7 +25,7 @@ specific to a CommandSet. for a more action-centric instead of object-centric command system while still organizing your code and handlers around the objects being managed. -See API documentation for `cmd2.command_definition.CommandSet`. +See API documentation for [cmd2.CommandSet][]. See [the examples](https://github.com/python-cmd2/cmd2/tree/main/examples/modular_commands) for more details. @@ -38,17 +38,18 @@ CommandSets group multiple commands together. The plugin will inspect functions `CommandSet` using the same rules as when they're defined in `cmd2.Cmd`. Commands must be prefixed with `do_`, help functions with `help_`, and completer functions with `complete_`. -A new decorator `with_default_category` is provided to categorize all commands within a CommandSet -in the same command category. Individual commands in a CommandSet may override the default category -by specifying a specific category with `cmd2.with_category`. +The [@with_default_category][cmd2.with_default_category] decorator is provided to categorize all +commands within a CommandSet class in the same command category. Individual commands in a CommandSet +class may override the default category by using the [@with_category][cmd2.with_category] decorator +on that method. CommandSet command methods will always expect the same parameters as when defined in a `cmd2.Cmd` sub-class, except that `self` will now refer to the `CommandSet` instead of the cmd2 instance. The cmd2 instance can be accessed through `self._cmd` that is populated when the `CommandSet` is registered. -CommandSets will only be auto-loaded if the constructor takes no arguments. If you need to provide -constructor arguments, see [Manual CommandSet Construction](#manual-commandset-construction). +CommandSets will only be auto-loaded if the initializer takes no arguments. If you need to provide +initializer arguments, see [Manual CommandSet Construction](#manual-commandset-construction). ```py import cmd2 @@ -78,8 +79,8 @@ class ExampleApp(cmd2.Cmd): ### Manual CommandSet Construction -If a CommandSet class requires parameters to be provided to the constructor, you may manually -construct CommandSets and pass in the constructor to Cmd2. +If a CommandSet class requires parameters to be provided to the initializer, you may manually +construct CommandSets and pass in the initializer to Cmd2. ```py import cmd2 @@ -101,7 +102,7 @@ class CustomInitCommandSet(CommandSet): class ExampleApp(cmd2.Cmd): """ - CommandSets with constructor parameters are provided in the constructor + CommandSets with initializer parameters are provided in the initializer """ def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize @@ -205,37 +206,38 @@ if __name__ == '__main__': ## Event Handlers -The following functions are called at different points in the `CommandSet` life cycle. +The following functions are called at different points in the [CommandSet][cmd2.CommandSet] life +cycle. -`on_register(self, cmd) -> None` - Called by cmd2.Cmd as the first step to registering a CommandSet. -The commands defined in this class have not be added to the CLI object at this point. Subclasses can -override this to perform any initialization requiring access to the Cmd object (e.g. configure -commands and their parsers based on CLI state data). +[on_register][cmd2.CommandSet.on_register] - Called by `cmd2.Cmd` as the first step to registering a +`CommandSet`. The commands defined in this class have not be added to the CLI object at this point. +Subclasses can override this to perform any initialization requiring access to the Cmd object (e.g. +configure commands and their parsers based on CLI state data). -`on_registered(self) -> None` - Called by cmd2.Cmd after a CommandSet is registered and all its -commands have been added to the CLI. Subclasses can override this to perform custom steps related to -the newly added commands (e.g. setting them to a disabled state). +[on_registered][cmd2.CommandSet.on_registered] - Called by `cmd2.Cmd` after a `CommandSet` is +registered and all its commands have been added to the CLI. Subclasses can override this to perform +custom steps related to the newly added commands (e.g. setting them to a disabled state). -`on_unregister(self) -> None` - Called by `cmd2.Cmd` as the first step to unregistering a -CommandSet. Subclasses can override this to perform any cleanup steps which require their commands -being registered in the CLI. +[on_unregister][cmd2.CommandSet.on_unregister] - Called by `cmd2.Cmd` as the first step to +unregistering a `CommandSet`. Subclasses can override this to perform any cleanup steps which +require their commands being registered in the CLI. -`on_unregistered(self) -> None` - Called by `cmd2.Cmd` after a CommandSet has been unregistered and -all its commands removed from the CLI. Subclasses can override this to perform remaining cleanup -steps. +[on_unregistered][cmd2.CommandSet.on_unregistered] - Called by `cmd2.Cmd` after a `CommandSet` has +been unregistered and all its commands removed from the CLI. Subclasses can override this to perform +remaining cleanup steps. ## Injecting Subcommands ### Description -Using the `with_argparse` decorator, it is possible to define subcommands for your command. This has -a tendency to either drive your interface into an object-centric interface. For example, imagine you -have a tool that manages your media collection and you want to manage movies or shows. An -object-centric approach would push you to have base commands such as `movies` and `shows` which each -have subcommands `add`, `edit`, `list`, `delete`. If you wanted to present an action-centric command -set, so that `add`, `edit`, `list`, and `delete` are the base commands, you'd have to organize your -code around these similar actions rather than organizing your code around similar objects being -managed. +Using the [@with_argparser][cmd2.with_argparser] and [@as_subcommand_to][cmd2.as_subcommand_to] +decorators, it is possible to easily define subcommands for your command. This has a tendency to +drive your interface into an object-centric interface. For example, imagine you have a tool that +manages your media collection and you want to manage movies or shows. An object-centric approach +would push you to have base commands such as `movies` and `shows` which each have subcommands `add`, +`edit`, `list`, `delete`. If you wanted to present an action-centric command set, so that `add`, +`edit`, `list`, and `delete` are the base commands, you'd have to organize your code around these +similar actions rather than organizing your code around similar objects being managed. Subcommand injection allows you to inject subcommands into a base command to present an interface that is sensible to a user while still organizing your code in whatever structure makes more logical From 3224336729da0f646d8da56fc1361b777998d17b Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 22:18:41 -0400 Subject: [PATCH 05/18] Finish making a detailed pass through the existing documentation --- docs/doc_conventions.md | 10 ++-- docs/examples/alternate_event_loops.md | 20 ++++---- docs/examples/getting_started.md | 26 +++++----- docs/features/multiline_commands.md | 10 ++-- docs/features/os.md | 33 ++++++++----- docs/features/packaging.md | 27 ++++------- docs/features/plugins.md | 30 +++++++----- docs/features/prompt.md | 24 ++++----- docs/features/redirection.md | 8 +-- docs/features/scripting.md | 59 ++++++++++++----------- docs/features/settings.md | 39 +++++++-------- docs/features/shortcuts_aliases_macros.md | 15 ++++-- docs/features/startup_commands.md | 12 +++-- docs/features/table_creation.md | 2 +- docs/features/transcripts.md | 4 +- docs/testing.md | 8 +-- examples/getting_started.py | 4 +- 17 files changed, 177 insertions(+), 154 deletions(-) diff --git a/docs/doc_conventions.md b/docs/doc_conventions.md index 1bc8dab3c..f6a2ad585 100644 --- a/docs/doc_conventions.md +++ b/docs/doc_conventions.md @@ -36,7 +36,7 @@ In Markdown all indenting is significant. Use 4 spaces per indenting level. ## Wrapping -Hard wrap all text so that line lengths are no greater than 120 characters. It makes everything +Hard wrap all text so that line lengths are no greater than 100 characters. It makes everything easier when editing documentation, and has no impact on reading documentation because we render to html. @@ -75,9 +75,13 @@ When using `mkdocstrings`, it must be preceded by a blank line before and after, ### Links to API Reference -To reference a method or function, do the following: +To reference a class, method, or function, do the following use block quotes around it followed by +empty block quotes. So to reference `cmd2.Cmd`, you using `[cmd2.Cmd][]`. -TODO: Figure out how to do this +If you want to change the name to use something shorter than the full namespace resolution you can +put the full path in the 2nd set of block quotes instead of leaving it empty and put the shorter +name in the one on the left. So you could also use `[Cmd][cmd2.Cmd]` to link to the API +documentation for `cmd2.Cmd`. ## Referencing cmd2 diff --git a/docs/examples/alternate_event_loops.md b/docs/examples/alternate_event_loops.md index dfd12d085..c3a3916cc 100644 --- a/docs/examples/alternate_event_loops.md +++ b/docs/examples/alternate_event_loops.md @@ -44,25 +44,25 @@ if __name__ == '__main__': app.postloop() ``` -The `cmd2.Cmd.runcmds_plus_hooks()` method runs multiple commands via -`cmd2.Cmd.onecmd_plus_hooks()`. +The [cmd2.Cmd.runcmds_plus_hooks][] method runs multiple commands where each single command is +executed via [cmd2.Cmd.onecmd_plus_hooks][]. -The `cmd2.Cmd.onecmd_plus_hooks()` method will do the following to execute a single command in a +The [cmd2.Cmd.onecmd_plus_hooks][] method will do the following to execute a single command in a normal fashion: -1. Parse user input into a `cmd2.Statement` object -1. Call methods registered with `cmd2.Cmd.register_postparsing_hook()` +1. Parse user input into a [cmd2.Statement][] object +1. Call methods registered with [cmd2.Cmd.register_postparsing_hook][] 1. Redirect output, if user asked for it and it's allowed 1. Start timer -1. Call methods registered with `cmd2.Cmd.register_precmd_hook` -1. Call `cmd2.Cmd.precmd` - for backwards compatibility with `cmd.Cmd` +1. Call methods registered with [cmd2.Cmd.register_precmd_hook][] +1. Call [cmd2.Cmd.precmd][] - for backwards compatibility with `cmd` 1. Add statement to [History](../features/history.md) 1. Call `do_command` method -1. Call methods registered with `cmd2.Cmd.register_postcmd_hook()` -1. Call `cmd2.Cmd.postcmd` - for backwards compatibility with `cmd.Cmd` +1. Call methods registered with [cmd2.Cmd.register_postcmd_hook][] +1. Call [cmd2.Cmd.postcmd][] - for backwards compatibility with `cmd` 1. Stop timer and display the elapsed time 1. Stop redirecting output if it was redirected -1. Call methods registered with `cmd2.Cmd.register_cmdfinalization_hook()` +1. Call methods registered with [cmd2.Cmd.register_cmdfinalization_hook][] Running in this fashion enables the ability to integrate with an external event loop. However, how to integrate with any specific event loop is beyond the scope of this documentation. Please note diff --git a/docs/examples/getting_started.md b/docs/examples/getting_started.md index 559e04a8f..e88d26974 100644 --- a/docs/examples/getting_started.md +++ b/docs/examples/getting_started.md @@ -1,6 +1,8 @@ # Getting Started -Here's a quick walkthrough of a simple application which demonstrates 10 features of `cmd2`: +Here's a quick walkthrough of a the simple +[getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) +example application which demonstrates many features of `cmd2`: - [Settings](../features/settings.md) - [Commands](../features/commands.md) @@ -29,36 +31,36 @@ following contents: ```py #!/usr/bin/env python -"""A simple cmd2 application.""" +"""A basic cmd2 application.""" import cmd2 -class FirstApp(cmd2.Cmd): - """A simple cmd2 application.""" +class BasicApp(cmd2.Cmd): + """Cmd2 application to demonstrate many common features.""" if __name__ == '__main__': import sys - c = FirstApp() - sys.exit(c.cmdloop()) + app = BasicApp() + sys.exit(app.cmdloop()) ``` -We have a new class `FirstApp` which is a subclass of [cmd2.Cmd][]. When we tell python to run our +We have a new class `BasicApp` which is a subclass of [cmd2.Cmd][]. When we tell Python to run our file like this: ```shell $ python getting_started.py ``` -it creates an instance of our class, and calls the `cmd2.Cmd.cmdloop` method. This method accepts -user input and runs commands based on that input. Because we subclassed `cmd2.Cmd`, our new app -already has a bunch of features built in. +The application creates an instance of our class, and calls the [cmd2.Cmd.cmdloop][] method. This +method accepts user input and runs commands based on that input. Because we subclassed `cmd2.Cmd`, +our new app already has a bunch of features built in. Congratulations, you have a working `cmd2` app. You can run it, and then type `quit` to exit. ## Create a New Setting -Before we create our first command, we are going to add a setting to this app. `cmd2` includes +Before we create our first command, we are going to add a new setting to this app. `cmd2` includes robust support for [Settings](../features/settings.md). You configure settings during object initialization, so we need to add an initializer to our class: @@ -89,7 +91,7 @@ Now we will create our first command, called `speak` which will echo back whatev say. We are going to use an [argument processor](../features/argument_processing.md) so the `speak` command can shout and talk pig latin. We will also use some built in methods for [generating output](../features/generating_output.md). Add this code to `getting_started.py`, so -that the `speak_parser` attribute and the `do_speak()` method are part of the `FirstApp()` class: +that the `speak_parser` attribute and the `do_speak()` method are part of the `BasicApp()` class: ```py speak_parser = cmd2.Cmd2ArgumentParser() diff --git a/docs/features/multiline_commands.md b/docs/features/multiline_commands.md index 8a4d76c18..79ad80031 100644 --- a/docs/features/multiline_commands.md +++ b/docs/features/multiline_commands.md @@ -1,10 +1,10 @@ # Multiline Commands Command input may span multiple lines for the commands whose names are listed in the -`multiline_commands` argument to `cmd2.Cmd.__init__()`. These commands will be executed only after -the user has entered a _terminator_. By default, the command terminator is `;`; specifying the -`terminators` optional argument to `cmd2.Cmd.__init__()` allows different terminators. A blank line -is _always_ considered a command terminator (cannot be overridden). +`multiline_commands` argument to [cmd2.Cmd.\_\_init\_\_][cmd2.Cmd.__init__]. These commands will be +executed only after the user has entered a _terminator_. By default, the command terminator is `;`. +Specifying the `terminators` optional argument to `cmd2.Cmd.__init__()` 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. @@ -14,7 +14,7 @@ arguments unless they appear after the terminator. When a user types a **Multiline Command** it may span more than one line of input. The prompt for the first line of input is specified by the [cmd2.Cmd.prompt][] instance attribute - see [Customizing the Prompt](./prompt.md#customizing-the-prompt). The prompt for subsequent lines of -input is defined by the `cmd2.Cmd.continuation_prompt` attribute. +input is defined by the [cmd2.Cmd.continuation_prompt][] attribute. ## Use cases diff --git a/docs/features/os.md b/docs/features/os.md index f7a11f8f8..4dad65b11 100644 --- a/docs/features/os.md +++ b/docs/features/os.md @@ -15,8 +15,14 @@ get a `!` shortcut for `shell`, which allows you to type: (Cmd) !ls -al -NOTE: `cmd2` provides user-friendly tab completion throughout the process of running a shell -command - first for the shell command name itself, and then for file paths in the argument section. +!!! note + + `cmd2` provides user-friendly tab completion throughout the process of running a shell command - + first for the shell command name itself, and then for file paths in the argument section. + + However, a `cmd2` application effectively **becomes** the shell, so if you have _extra_ shell + completion configured for your particular shell such as `bash`, `zsh`, `fish`, etc. then this + will not be available within `cmd2`. ## Editors @@ -36,16 +42,18 @@ system. ## Terminal pagers -Output of any command can be displayed one page at a time using the `cmd2.Cmd.ppaged` method. +Output of any command can be displayed one page at a time using the [cmd2.Cmd.ppaged][] method. Alternatively, a terminal pager can be invoked directly using the ability to run shell commands with the `!` shortcut like so: (Cmd) !less foo.txt -NOTE: Once you are in a terminal pager, that program temporarily has control of your terminal, -**NOT** `cmd2`. Typically you can use either the arrow keys or ``/`` keys to -scroll around or type `q` to quit the pager and return control to your `cmd2` application. +!!! warning + + Once you are in a terminal pager, that program temporarily has control of your terminal, + **NOT** `cmd2`. Typically you can use either the arrow keys or ``/`` keys to + scroll around or type `q` to quit the pager and return control to your `cmd2` application. ## Exit codes @@ -87,10 +95,10 @@ shell, and execute those commands before entering the command loop: $ python examples/transcript_example.py help - Documented commands (use 'help -v' for verbose/'help ' for details): - =========================================================================== - alias help macro orate quit run_script set shortcuts - edit history mumble py run_pyscript say shell speak + Documented Commands + ─────────────────── + alias help macro orate run_pyscript say shell speak + edit history mumble quit run_script set shortcuts (Cmd) @@ -111,8 +119,9 @@ shell, but have it say it in pig latin: Uh-oh, that's not what we wanted. `cmd2` treated `-p`, `hello`, and `there` as commands, which don't exist in that program, thus the syntax errors. -There is an easy way around this, which is demonstrated in `examples/cmd_as_argument.py`. By setting -`allow_cli_args=False` you can do your own argument parsing of the command line: +There is an easy way around this, which is demonstrated in +[cmd_as_argument.py](https://github.com/python-cmd2/cmd2/blob/main/examples/cmd_as_argument.py) +example. By setting `allow_cli_args=False` you can do your own argument parsing of the command line: $ python examples/cmd_as_argument.py speak -p hello there ellohay heretay diff --git a/docs/features/packaging.md b/docs/features/packaging.md index a2a67bdf4..16d77436e 100644 --- a/docs/features/packaging.md +++ b/docs/features/packaging.md @@ -16,7 +16,8 @@ using idiomatic Python packaging tools such as [pip](https://pip.pypa.io/) or [uv](https://github.com/astral-sh/uv). Small tweaks on this process can allow you to publish to private PyPI mirrors such as one hosted on -[AWS CodeArtifact](https://aws.amazon.com/codeartifact/). +[AWS CodeArtifact](https://aws.amazon.com/codeartifact/) or a private +[Artifactory](https://jfrog.com/artifactory/) server. ## Packaging your application in a container using Docker @@ -33,26 +34,18 @@ This convenient blog post will show you For developers wishing to package a `cmd2` application into a single binary image or compressed file, we can recommend all of the following based on personal and professional experience: +- [Nuitka](https://github.com/Nuitka/Nuitka) + - Nuitka is a Python compiler written in Python + - You feed it your Python app, it does a lot of clever things, and spits out an executable or + extension module + - Particularly convenient if you have IP you wish to protect by obfuscating the Python source + code behind your application - [PyInstaller](https://www.pyinstaller.org) - Freeze (package) Python programs into stand-alone executables - PyInstaller bundles a Python application and all its dependencies into a single package - The user can run the packaged app without installing a Python interpreter or any modules -- [Nuitka](https://nuitka.net) - - Nuitka is a Python compiler written in Python - - You feed it your Python app, it does a lot of clever things, and spits out an executable or - extension module - - This can be particularly convenient if you wish to obfuscate the Python source code behind - your application -- [Conda Constructor](https://github.com/conda/constructor) - - Allows you to create a custom Python distro based on - [Miniconda](https://docs.conda.io/en/latest/miniconda.html) - [PyOxidizer](https://github.com/indygreg/PyOxidizer) - - PyOxidizer is a utility for producing binaries that embed Python - - PyOxidizer is capable of producing a single file executable - with a copy of Python and all - its dependencies statically linked and all resources embedded in the executable + - A modern Python application packaging and distribution tool implemented in Rust + - A utility for producing binaries that embed Python and all of your dependencies - You can copy a single executable file to another machine and run a Python application contained within. It just works. - -!!! warning - - We haven't personally tested PyOxidizer with `cmd2` applications like everything else on this page, though we have heard good things about it diff --git a/docs/features/plugins.md b/docs/features/plugins.md index 566f84410..e5ea37b2e 100644 --- a/docs/features/plugins.md +++ b/docs/features/plugins.md @@ -6,7 +6,7 @@ extend basic `cmd2` functionality and can be used by multiple applications. There are many ways to add functionality to `cmd2` using a plugin. Most plugins will be implemented as a mixin. A mixin is a class that encapsulates and injects code into another class. Developers who use a plugin in their `cmd2` project will inject the plugin's code into their subclass of -`cmd2.Cmd`. +[cmd2.Cmd][]. ## Mixin and Initialization @@ -38,15 +38,17 @@ class Example(cmd2_myplugin.MyPlugin, cmd2.Cmd): # all plugins have initialized ``` -Note how the plugin must be inherited (or mixed in) before `cmd2.Cmd`. This is required for two -reasons: +!!! warning -- The `cmd.Cmd.__init__` method in the Python standard library does not call `super().__init__()`. - Because of this oversight, if you don't inherit from `MyPlugin` first, the `MyPlugin.__init__()` - method will never be called. -- You may want your plugin to be able to override methods from `cmd2.Cmd`. If you mixin the plugin - after `cmd2.Cmd`, the Python method resolution order will call [cmd2.Cmd][] methods before it - calls those in your plugin. + The plugin must be inherited (or mixed in) before `cmd2.Cmd`. This is required for two + reasons: + + - The `cmd.Cmd.__init__` method in the Python standard library does not call `super().__init__()`. + Because of this oversight, if you don't inherit from `MyPlugin` first, the `MyPlugin.__init__()` + method will never be called. + - You may want your plugin to be able to override methods from `cmd2.Cmd`. If you mixin the plugin + after `cmd2.Cmd`, the Python method resolution order will call [cmd2.Cmd][] methods before it + calls those in your plugin. ## Add commands @@ -77,7 +79,7 @@ class MyPlugin: self.add_settable(cmd2.Settable('mysetting', str, 'short help message for mysetting', self)) ``` -You can hide settings from the user by calling `cmd2.Cmd.remove_settable`. See +You can hide settings from the user by calling [cmd2.Cmd.remove_settable][]. See [Settings](./settings.md) for more information. ## Decorators @@ -97,8 +99,9 @@ Hooks are a much better approach. ## Hooks Plugins can register hook methods, which are called by [cmd2.Cmd][] during various points in the -application and command processing lifecycle. Plugins should not override any of the deprecated hook -methods, instead they should register their hooks as described in the [Hooks](./hooks.md) section. +application and command processing lifecycle. Plugins should not override any of the `cmd` base +class hook methods, instead they should register their hooks as described in the [Hooks](./hooks.md) +section. You should name your hooks so that they begin with the name of your plugin. Hook methods get mixed into the `cmd2` application and this naming convention helps avoid unintentional method overriding. @@ -134,4 +137,5 @@ will know what's available. ## Examples -See for more info. +See [cmd2 Plugin Template](https://github.com/python-cmd2/cmd2/tree/main/plugins/template) for more +info. diff --git a/docs/features/prompt.md b/docs/features/prompt.md index 76dbc2ade..a08d6b6c4 100644 --- a/docs/features/prompt.md +++ b/docs/features/prompt.md @@ -4,10 +4,10 @@ ## Customizing the Prompt -This prompt can be configured by setting the `cmd2.Cmd.prompt` instance attribute. This contains the -string which should be printed as a prompt for user input. See the -[getting_started](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) example -for the simple use case of statically setting the prompt. +This prompt can be configured by setting the [cmd2.Cmd.prompt][] instance attribute. This contains +the string which should be printed as a prompt for user input. See the +[getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) +example for the simple use case of statically setting the prompt. ## Continuation Prompt @@ -15,24 +15,24 @@ When a user types a [Multiline Command](./multiline_commands.md) it may span mor input. The prompt for the first line of input is specified by the `cmd2.Cmd.prompt` instance attribute. The prompt for subsequent lines of input is defined by the `cmd2.Cmd.continuation_prompt` attribute. See the -[getting_started](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) example -for a demonstration of customizing the continuation prompt. +[getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) +example for a demonstration of customizing the continuation prompt. ## Updating the prompt If you wish to update the prompt between commands, you can do so using one of the [Application Lifecycle Hooks](./hooks.md#application-lifecycle-hooks) such as a [Postcommand hook](./hooks.md#postcommand-hooks). See -[PythonScripting](https://github.com/python-cmd2/cmd2/blob/main/examples/python_scripting.py) for an -example of dynamically updating the prompt. +[python_scripting.py](https://github.com/python-cmd2/cmd2/blob/main/examples/python_scripting.py) +for an example of dynamically updating the prompt. ## Asynchronous Feedback `cmd2` provides these 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. +supports [VT100](https://en.wikipedia.org/wiki/VT100) control characters and `readline`. Linux, Mac, +and Windows 10 and greater all support these. ::: cmd2.Cmd.async_alert @@ -49,5 +49,5 @@ Windows 10 and greater all support these. ::: cmd2.Cmd.set_window_title The easiest way to understand these functions is to see the -[AsyncPrinting](https://github.com/python-cmd2/cmd2/blob/main/examples/async_printing.py) example -for a demonstration. +[async_printing.py](https://github.com/python-cmd2/cmd2/blob/main/examples/async_printing.py) +example for a demonstration. diff --git a/docs/features/redirection.md b/docs/features/redirection.md index e9cf6c982..8b2ab68e4 100644 --- a/docs/features/redirection.md +++ b/docs/features/redirection.md @@ -58,7 +58,9 @@ output of that to a file called _output.txt_. ## Limitations of Redirection -Some limitations apply to redirection and piping within `cmd2` applications: +!!! warning -- Can only pipe to shell commands, not other `cmd2` application commands -- **stdout** gets redirected/piped, **stderr** does not + Some limitations apply to redirection and piping within `cmd2` applications: + + - Can only pipe to shell commands, not other `cmd2` application commands + - **stdout** gets redirected/piped, **stderr** does not diff --git a/docs/features/scripting.md b/docs/features/scripting.md index acad9581b..a656658fd 100644 --- a/docs/features/scripting.md +++ b/docs/features/scripting.md @@ -33,10 +33,10 @@ the first script. ### Comments -Any command line input where the first non-whitespace character is a `\#` will be treated as a -comment. This means any `\#` character appearing later in the command will be treated as a literal. -The same applies to a `\#` in the middle of a multiline command, even if it is the first character -on a line. +Any command line input where the first non-whitespace character is a `#` will be treated as a +comment. This means any `#` character appearing later in the command will be treated as a literal. +The same applies to a `#` in the middle of a multiline command, even if it is the first character on +a line. Comments are useful in scripts, but would be pointless within an interactive session. @@ -48,7 +48,8 @@ Comments are useful in scripts, but would be pointless within an interactive ses If you require logic flow, loops, branching, or other advanced features, you can write a python script which executes in the context of your `cmd2` app. This script is run using the [run_pyscript](./builtin_commands.md#run_pyscript) command. Here's a simple example that uses the -[arg_printer](https://github.com/python-cmd2/cmd2/blob/main/examples/scripts/arg_printer.py) script: +[arg_printer.py](https://github.com/python-cmd2/cmd2/blob/main/examples/scripts/arg_printer.py) +pyscript: (Cmd) run_pyscript examples/scripts/arg_printer.py foo bar 'baz 23' Running Python script 'arg_printer.py' which was called with 3 arguments @@ -70,12 +71,13 @@ be run by pyscript and "designer" as the `cmd2` application author. ### Basics Without any work on the part of the designer, a scripter can take advantage of piecing together -workflows using simple `app` calls. The result of a `run_pyscript` app call yields a `CommandResult` -object exposing four members: `Stdout`, `Stderr`, `Stop`, and `Data`. +workflows using simple `app` calls. The result of a `run_pyscript` app call yields a +[CommandResult][cmd2.CommandResult] object exposing four members: `stdout`, `stderr`, `stop`, and +`data`. -`Stdout` and `Stderr` are fairly straightforward representations of normal data streams and -accurately reflect what is seen by the user during normal cmd2 interaction. `Stop` contains -information about how the invoked command has ended its lifecycle. Lastly `Data` contains any +`stdout` and `stderr` are fairly straightforward representations of normal data streams and +accurately reflect what is seen by the user during normal cmd2 interaction. `stop` contains +information about how the invoked command has ended its lifecycle. Lastly `data` contains any information the designer sets via `self.last_result` or `self._cmd.last_result` if called from inside a CommandSet. @@ -101,19 +103,20 @@ second = 'second' app(f'command {first} -t {second}) ``` -See [python_scripting](https://github.com/python-cmd2/cmd2/blob/main/examples/python_scripting.py) +See +[python_scripting.py](https://github.com/python-cmd2/cmd2/blob/main/examples/python_scripting.py) example and associated -[conditional](https://github.com/python-cmd2/cmd2/blob/main/examples/scripts/conditional.py) script -for more information. +[conditional.py](https://github.com/python-cmd2/cmd2/blob/main/examples/scripts/conditional.py) +script for more information. ### Design principles -If the cmd2 application follows the -[unix_design_philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) a scriptor will have the -most flexibility to piece together workflows using different commands. If the designer\'s -application is more complete and less likely to be augmented in the future a scripter may opt for -simple serial scripts with little control flow. In either case, choices made by the designer will -have effects on scripters. +If your cmd2 application follows the +[unix_design_philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) a scripter will have the +most flexibility to piece together workflows using different commands. If the designer's application +is more complete and less likely to be augmented in the future a scripter may opt for simple serial +scripts with little control flow. In either case, choices made by the designer will have effects on +scripters. The following diagram illustrates the different boundaries to keep in mind. @@ -141,14 +144,14 @@ flowchart LR !!! warning - It is bad design for a high level pyscript to know about let alone access low level class libraries of an application. Resist this urge at all costs, unless it\'s necessary. + It is bad design for a high level pyscript to know about, let alone access, low level class libraries of an application. Resist this urge at all costs, unless it's necessary. ### Developing a Basic API -CMD2 out of the box allows scripters to take advantage of all exposed `do_*` commands. As a scripter -one can easily interact with the application via `stdout` and `stderr`. +`cmd2` out of the box allows scripters to take advantage of all exposed `do_*` commands. As a +scripter one can easily interact with the application via `stdout` and `stderr`. -As a baseline lets start off with the familiar FirstApp +As a baseline lets start off with the the following `cmd2` application called `FirstApp` ```py #!/usr/bin/env python @@ -221,7 +224,7 @@ When executing the `speak` command without parameters you see the following erro Usage: speak [-h] [-p] [-s] [-r REPEAT] words [...] Error: the following arguments are required: words -Even though this is a fully qualified CMD2 error the pyscript must look for this error and perform +Even though this is a fully qualified `cmd2` error the pyscript must look for this error and perform error checking.: ```py @@ -304,7 +307,7 @@ accommodate the weary scripter by adding one small line at the end of our `do_*` Adding the above line supercharges a cmd2 application and opens a new world of possibilities. -!!! note +!!! tip When setting results for a command function inside of a CommandSet use the private cmd instance: @@ -356,11 +359,11 @@ immutable over mutable types and never provide direct access to class members as potentially lead to violation of the [open_closed_principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). -When possible, a dataclass is a lightweight solution perfectly suited for data manipulation. Lets -dive into an example. +When possible, a frozen dataclass is a lightweight solution perfectly suited for data manipulation. +Lets dive into an example. The following fictional application has two commands: `build` and `status`. We can pretend that the -build action happens somewhere else in the world at an REST API endpoint and has significant +build action happens somewhere else in the world at a REST API endpoint and has significant computational cost. The status command for all intents and purposes, will only show the current status of a build task. The application has provided all that is needed for a user to start a build and then determine its status. The problem however is that with a long running process the user may diff --git a/docs/features/settings.md b/docs/features/settings.md index 649dd0cbf..a4c58eb9f 100644 --- a/docs/features/settings.md +++ b/docs/features/settings.md @@ -1,11 +1,10 @@ # Settings -Settings provide a mechanism for a user to control the behavior of a `cmd2` based application. A -setting is stored in an instance attribute on your subclass of `cmd2.Cmd` and must also appear in -the `cmd2.Cmd.settable` dictionary. Developers may set default values for these settings and users -can modify them at runtime using the [set](./builtin_commands.md#set) command. Developers can -[Create New Settings](#create-new-settings) and can also -[Hide Builtin Settings](#hide-builtin-settings) from the user. +Settings provide a mechanism for a user to control the behavior of a `cmd2` based application. +Settings are stored in a protected instance attribute on your subclass of [cmd2.Cmd][]. Developers +may set default values for these settings and users can view and modify them at runtime using the +[set](./builtin_commands.md#set) command. Developers can [Create New Settings](#create-new-settings) +and can also [Hide Builtin Settings](#hide-builtin-settings) from the user. ## Builtin Settings @@ -97,17 +96,19 @@ Your application can define user-settable parameters which your code can referen initialization code: 1. Create an instance attribute with a default value -1. Create a `Settable` object which describes your setting -1. Pass the `Settable` object to `cmd2.Cmd.add_settable` +1. Create a [Settable][cmd2.Settable] object which describes your setting +1. Pass the `Settable` object to the [add_settable][cmd2.Cmd.add_settable] method Here's an example, from [examples/environment.py](https://github.com/python-cmd2/cmd2/blob/main/examples/environment.py): -```py -{% - include "../../examples/environment.py" -%} -``` +!!! example "examples/environment.py" + + ```py + {% + include "../../examples/environment.py" + %} + ``` If you want to be notified when a setting changes (as we do above), then be sure to supply a method to the `onchange_cb` parameter of the `cmd2.utils.Settable`. This method will be called after the @@ -136,14 +137,10 @@ It's 13 C - are you a penguin? ## Hide Builtin Settings -You may want to prevent a user from modifying a built-in setting. A setting must appear in the -`cmd2.Cmd.settable` dictionary in order for it to be available to the -[set](./builtin_commands.md#set) command. - -Let's say that you never want end users of your program to be able to enable full debug tracebacks -to print out if an error occurs. You might want to hide the [debug](#debug) setting. To do so, -remove it from the `cmd2.Cmd.settable` dictionary after you initialize your object. The -`cmd2.Cmd.remove_settable` convenience method makes this easy: +You may want to prevent a user from modifying a built-in setting. Let's say that you never want end +users of your program to be able to enable full debug tracebacks to print out if an error occurs. +You might want to hide the [debug](#debug) setting. The [cmd2.Cmd.remove_settable][] method makes +this easy: ```py class MyApp(cmd2.Cmd): diff --git a/docs/features/shortcuts_aliases_macros.md b/docs/features/shortcuts_aliases_macros.md index 47c2586ea..ca6fc7426 100644 --- a/docs/features/shortcuts_aliases_macros.md +++ b/docs/features/shortcuts_aliases_macros.md @@ -11,13 +11,14 @@ default, the following shortcuts are defined: - **`@`** - run script file - **`@@`** - run script file; filename is relative to current script location -To define more shortcuts, update the dict `App.shortcuts` with the `{'shortcut': 'command_name'}` -(omit `do_`): +To define more shortcuts, stat with the [cmd2.DEFAULT_SHORTCUTS][] constant which is a dictionary +and then add more shortcuts to it by updating it with a dictionary of additional shortcuts in the +format `{'shortcut': 'command_name'}` where you omit `do_` from the command name: ```py class App(Cmd): def __init__(self): - shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) + shortcuts = cmd2.DEFAULT_SHORTCUTS shortcuts.update({'*': 'sneeze', '~': 'squirm'}) cmd2.Cmd.__init__(self, shortcuts=shortcuts) ``` @@ -26,7 +27,9 @@ class App(Cmd): Shortcuts need to be created by updating the `shortcuts` dictionary attribute prior to calling the `cmd2.Cmd` super class `__init__()` method. Moreover, that super class init method needs to be called after updating the `shortcuts` attribute This warning applies in general to many other attributes which are not settable at runtime. -Note: Command, alias, and macro names cannot start with a shortcut +!!! tip + + Command, alias, and macro names cannot start with a shortcut ## Aliases @@ -94,4 +97,6 @@ For more details on listing macros run: `help macro list` For more details on deleting macros run: `help macro delete` -Note: Macros cannot have the same name as a command or alias +!!! warning + + Macros cannot have the same name as a command or alias diff --git a/docs/features/startup_commands.md b/docs/features/startup_commands.md index 1bd563abc..87daf0bc9 100644 --- a/docs/features/startup_commands.md +++ b/docs/features/startup_commands.md @@ -26,7 +26,7 @@ application and easily used in automation. !!! note - If you wish to disable cmd2's consumption of command-line arguments, you can do so by setting the `allow_cli_args` argument of your `cmd2.Cmd` class instance to `False`. This would be useful, for example, if you wish to use something like [Argparse](https://docs.python.org/3/library/argparse.html) to parse the overall command line arguments for your application: + If you wish to disable cmd2's consumption of command-line arguments, you can do so by setting the `allow_cli_args` argument of your [cmd2.Cmd][] class instance to `False`. This would be useful, for example, if you wish to use something like [argparse](https://docs.python.org/3/library/argparse.html) to parse the overall command line arguments for your application: ```py from cmd2 import Cmd @@ -47,8 +47,8 @@ class StartupApp(cmd2.Cmd): ``` This text file should contain a [Command Script](./scripting.md#command-scripts). See the -[initialization](https://github.com/python-cmd2/cmd2/blob/main/examples/initialization.py) example -for a demonstration. +[getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) +example for a demonstration. You can silence a startup script's output by setting `silence_startup_script` to True: @@ -56,5 +56,7 @@ You can silence a startup script's output by setting `silence_startup_script` to cmd2.Cmd.__init__(self, startup_script='.cmd2rc', silence_startup_script=True) ``` -Anything written to stderr will still print. Additionally, a startup script cannot be silenced if -`allow_redirection` is False since silencing works by redirecting a script's output to `os.devnull`. +!!! warning + + Anything written to `stderr` will still print for a "silenced" startup script. Additionally, a startup script cannot be silenced if + `allow_redirection` is False since silencing works by redirecting a script's output to `os.devnull`. diff --git a/docs/features/table_creation.md b/docs/features/table_creation.md index 4a54cadd4..ff4a88348 100644 --- a/docs/features/table_creation.md +++ b/docs/features/table_creation.md @@ -1,6 +1,6 @@ # Table Creation -As of version 3, `cmd2` no longer includes code for table creation. +As of version 3.0.0, `cmd2` no longer includes custom code for table creation. This is because `cmd2` now has a dependency on [rich](https://github.com/Textualize/rich) which has excellent support for this feature. diff --git a/docs/features/transcripts.md b/docs/features/transcripts.md index 6404c035d..4e3f2bca5 100644 --- a/docs/features/transcripts.md +++ b/docs/features/transcripts.md @@ -2,7 +2,7 @@ A transcript is both the input and output of a successful session of a `cmd2`-based app which is saved to a text file. With no extra work on your part, your app can play back these transcripts as a -unit test. Transcripts can contain regular expressions, which provide the flexibility to match +regression test. Transcripts can contain regular expressions, which provide the flexibility to match responses from commands that produce dynamic or variable output. ## Creating From History @@ -23,7 +23,7 @@ This is by far the easiest way to generate a transcript. !!! warning - Make sure you use the **poutput()** method in your `cmd2` application for generating command output. This method of the `cmd2.Cmd` class ensures that output is properly redirected when redirecting to a file, piping to a shell command, and when generating a transcript. + Make sure you use the **poutput()** method in your `cmd2` application for generating command output. This method of the [cmd2.Cmd][] class ensures that output is properly redirected when redirecting to a file, piping to a shell command, and when generating a transcript. ## Creating From A Script File diff --git a/docs/testing.md b/docs/testing.md index 7c5875586..8ba1a5837 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -2,7 +2,7 @@ ## Overview -This covers special considerations when writing unit tests for a cmd2 application. +This covers special considerations when writing unit or integration tests for a cmd2 application. ## Testing Commands @@ -13,7 +13,7 @@ function captures and returns stdout, stderr, and the command-specific result da ## Mocking If you need to mock anything in your cmd2 application, and most specifically in sub-classes of -`cmd2.Cmd` or `cmd2.command_definition.CommandSet`, you must use +[cmd2.Cmd][] or [cmd2.CommandSet][], you must use [Autospeccing](https://docs.python.org/3/library/unittest.mock.html#autospeccing), [spec=True](https://docs.python.org/3/library/unittest.mock.html#patch), or whatever equivalent is provided in the mocking library you're using. @@ -24,8 +24,8 @@ automatically create mock objects to match any attribute being requested, regard they're present in the object being mocked. This behavior can incorrectly instruct cmd2 to treat a function or attribute as something it needs to recognize and process. To prevent this, you should always mock with [Autospeccing](https://docs.python.org/3/library/unittest.mock.html#autospeccing) -or [spec=True](https://docs.python.org/3/library/unittest.mock.html#patch enabled. If you don't have -autospeccing on, your unit tests will fail with an error message like: +or [spec=True](https://docs.python.org/3/library/unittest.mock.html#patch) enabled. If you don't +have autospeccing on, your unit tests will fail with an error message like: ```sh cmd2.exceptions.CommandSetRegistrationError: Subcommand diff --git a/examples/getting_started.py b/examples/getting_started.py index 43e9a904f..025a4f5c5 100755 --- a/examples/getting_started.py +++ b/examples/getting_started.py @@ -100,5 +100,7 @@ def do_echo(self, arg: cmd2.Statement) -> None: if __name__ == '__main__': + import sys + app = BasicApp() - app.cmdloop() + sys.exit(app.cmdloop()) From a43764c16ab99af3b2d0cfe97a4f318e0de5de99 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 22:33:03 -0400 Subject: [PATCH 06/18] Fix a couple typos found by gemini-cli code review --- docs/doc_conventions.md | 4 ++-- docs/features/argument_processing.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/doc_conventions.md b/docs/doc_conventions.md index f6a2ad585..e03e128b6 100644 --- a/docs/doc_conventions.md +++ b/docs/doc_conventions.md @@ -75,8 +75,8 @@ When using `mkdocstrings`, it must be preceded by a blank line before and after, ### Links to API Reference -To reference a class, method, or function, do the following use block quotes around it followed by -empty block quotes. So to reference `cmd2.Cmd`, you using `[cmd2.Cmd][]`. +To reference a class, method, or function, use block quotes around the name of the full namespace +path for it followed by empty block quotes. So to reference `cmd2.Cmd`, you use `[cmd2.Cmd][]`. If you want to change the name to use something shorter than the full namespace resolution you can put the full path in the 2nd set of block quotes instead of leaving it empty and put the shorter diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index b65f4ad94..16df6cc5d 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -277,7 +277,7 @@ def settings_ns_provider(self) -> argparse.Namespace: return ns ``` -To use this function with the `@2ith_argparser` decorator, do the following: +To use this function with the `@with_argparser` decorator, do the following: ```py @with_argparser(my_parser, ns_provider=settings_ns_provider) From 692fbbd1c05bd4abe58276b81a9ab41d10e3e248 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 22:41:28 -0400 Subject: [PATCH 07/18] Fixed a couple typos find by gemini-cli review --- docs/features/scripting.md | 6 +++--- docs/features/shortcuts_aliases_macros.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/features/scripting.md b/docs/features/scripting.md index a656658fd..f6e85f83c 100644 --- a/docs/features/scripting.md +++ b/docs/features/scripting.md @@ -151,7 +151,7 @@ flowchart LR `cmd2` out of the box allows scripters to take advantage of all exposed `do_*` commands. As a scripter one can easily interact with the application via `stdout` and `stderr`. -As a baseline lets start off with the the following `cmd2` application called `FirstApp` +As a baseline let's start off with the the following `cmd2` application called `FirstApp` ```py #!/usr/bin/env python @@ -198,7 +198,7 @@ if __name__ == '__main__': sys.exit(c.cmdloop()) ``` -Lets start off on the wrong foot: +Let's start off on the wrong foot: ```py app('speak' @@ -360,7 +360,7 @@ potentially lead to violation of the [open_closed_principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). When possible, a frozen dataclass is a lightweight solution perfectly suited for data manipulation. -Lets dive into an example. +Let's dive into an example. The following fictional application has two commands: `build` and `status`. We can pretend that the build action happens somewhere else in the world at a REST API endpoint and has significant diff --git a/docs/features/shortcuts_aliases_macros.md b/docs/features/shortcuts_aliases_macros.md index ca6fc7426..aff2b5efd 100644 --- a/docs/features/shortcuts_aliases_macros.md +++ b/docs/features/shortcuts_aliases_macros.md @@ -11,7 +11,7 @@ default, the following shortcuts are defined: - **`@`** - run script file - **`@@`** - run script file; filename is relative to current script location -To define more shortcuts, stat with the [cmd2.DEFAULT_SHORTCUTS][] constant which is a dictionary +To define more shortcuts, start with the [cmd2.DEFAULT_SHORTCUTS][] constant which is a dictionary and then add more shortcuts to it by updating it with a dictionary of additional shortcuts in the format `{'shortcut': 'command_name'}` where you omit `do_` from the command name: From 71e8ae724cac52999923b1bb10f42b7588b52225 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 23:11:50 -0400 Subject: [PATCH 08/18] Minor wording improvements --- docs/features/argument_processing.md | 33 +++--- docs/features/scripting.md | 123 +++++++++++----------- docs/features/shortcuts_aliases_macros.md | 26 ++--- 3 files changed, 91 insertions(+), 91 deletions(-) diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index 16df6cc5d..97eb64ba8 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -39,7 +39,7 @@ command which might have its own argument parsing. For each command in the `cmd2.Cmd` subclass which requires argument parsing, create a unique instance of `argparse.ArgumentParser()` which can parse the input appropriately for the command. Then decorate the command method with the `@with_argparser` decorator, passing the argument parser -as the first parameter to the decorator. This changes the second argument to the command method, +as the first parameter to the decorator. This changes the second argument of the command method, which will contain the results of `ArgumentParser.parse_args()`. Here's what it looks like: @@ -68,10 +68,11 @@ def do_speak(self, opts): !!! note - `cmd2` sets the `prog` variable in the argument parser based on the name of the method it is decorating. This will override anything you specify in `prog` variable when creating the argument parser. + `cmd2` sets the `prog` variable in the argument parser based on the name of the method it is decorating. + This will override anything you specify in `prog` variable when creating the argument parser. - As of the 3.0.0 release, `cmd2` sets `prog` when the instance-specific parser is created, which is - later than it did previously. + As of the 3.0.0 release, `cmd2` sets `prog` when the instance-specific parser is created, which is later + than in previous versions. ## Help Messages @@ -110,7 +111,7 @@ optional arguments: ``` If you would prefer, you can set the `description` while instantiating the `argparse.ArgumentParser` -and leave the docstring on your method empty: +and leave the docstring on your method blank: ```py from cmd2 import Cmd2ArgumentParser, with_argparser @@ -226,8 +227,8 @@ class CmdLineApp(cmd2.Cmd): ## Unknown Positional Arguments -If you want all unknown arguments to be passed to your command as a list of strings, then decorate -the command method with the `@with_argparser(..., with_unknown_args=True)` decorator. +To pass all unknown arguments to be passed to your command as a list of strings, then decorate the +command method with the `@with_argparser(..., with_unknown_args=True)` decorator. Here's what it looks like: @@ -256,8 +257,8 @@ def do_dir(self, args, unknown): ## Using A Custom Namespace -In some cases, it may be necessary to write custom `argparse` code that is dependent on state data -of your application. To support this ability while still allowing use of the decorators, +In some cases, it may be necessary to write custom `argparse` code that is dependent on your +application's state data. To support this ability while still allowing use of the decorators, `@with_argparser` has an optional argument called `ns_provider`. `ns_provider` is a Callable that accepts a `cmd2.Cmd` object as an argument and returns an @@ -283,8 +284,8 @@ To use this function with the `@with_argparser` decorator, do the following: @with_argparser(my_parser, ns_provider=settings_ns_provider) ``` -The Namespace is passed by the decorators to the `argparse` parsing functions which gives your -custom code access to the state data it needs for its parsing logic. +The Namespace is passed by the decorators to the `argparse` parsing functions, giving your custom +code access to the state data it needs for its parsing logic. ## Subcommands @@ -313,9 +314,9 @@ The [@as_subcommand_to][cmd2.as_subcommand_to] decorator makes adding subcommand ## Decorator Order If you are using custom decorators in combination with `@cmd2.with_argparser`, then the order of -your custom decorator(s) relative to the `cmd2` decorator matters when it comes to runtime behavior -and `argparse` errors. There is nothing `cmd2`-specific here, this is just a side-effect of how -decorators work in Python. To learn more about how decorators work, see +your custom decorator(s) relative to the `cmd2` decorator affects runtime behavior and `argparse` +errors. There is nothing `cmd2`-specific here, this is just a side-effect of how decorators work in +Python. To learn more about how decorators work, see [decorator_primer](https://realpython.com/primer-on-python-decorators). If you want your custom decorator's runtime behavior to occur in the case of an `argparse` error, @@ -329,8 +330,8 @@ def do_foo(self, args: argparse.Namespace) -> None: pass ``` -However, if you do NOT want the custom decorator runtime behavior to occur even in the case of an -`argparse` error, then that decorator needs to go **before** the `argparse` one, e.g.: +However, if you do NOT want the custom decorator runtime behavior to occur during an `argparse` +error, then that decorator needs to go **before** the `argparse` one, e.g.: ```py @my_decorator diff --git a/docs/features/scripting.md b/docs/features/scripting.md index f6e85f83c..c05789f7f 100644 --- a/docs/features/scripting.md +++ b/docs/features/scripting.md @@ -1,8 +1,8 @@ # Scripting -Operating system shells have long had the ability to execute a sequence of commands saved in a text -file. These script files make long sequences of commands easier to repeatedly execute. `cmd2` -supports two similar mechanisms: command scripts and python scripts. +Operating system shells have long been able to execute a sequence of commands saved in a text file. +These script files simplify the repeated execution of long command sequences. `cmd2` supports two +similar mechanisms: command scripts and python scripts. ## Command Scripts @@ -25,7 +25,7 @@ it inside a `cmd2` application. Command script files can be executed using the built-in [run_script](./builtin_commands.md#run_script) command or the `@` shortcut (if your application is -using the default shortcuts). Both ASCII and UTF-8 encoded unicode text files are supported. The +using the default shortcuts). Both ASCII and UTF-8 encoded Unicode text files are supported. The [run_script](./builtin_commands.md#run_script) command supports tab completion of file system paths. There is a variant [\_relative_run_script](./builtin_commands.md#_relative_run_script) command or `@@` shortcut (if using the default shortcuts) for use within a script which uses paths relative to @@ -33,12 +33,11 @@ the first script. ### Comments -Any command line input where the first non-whitespace character is a `#` will be treated as a -comment. This means any `#` character appearing later in the command will be treated as a literal. -The same applies to a `#` in the middle of a multiline command, even if it is the first character on -a line. +A command line is a comment if the first non-whitespace character is a `#`. This means any `#` +character appearing later in the command will be treated as a literal. The same applies to a `#` in +the middle of a multiline command, even if it is the first character on a line. -Comments are useful in scripts, but would be pointless within an interactive session. +Comments are useful in scripts, but are generally not used within an interactive session. (Cmd) # this is a comment (Cmd) command # this is not a comment @@ -62,11 +61,11 @@ as shown above it has the ability to pass command-line arguments to the scripts ## Developing a cmd2 API -If you as an app designer have not explicitly disabled the `run_pyscript` command it must be assumed -that your application is structured for use in higher level python scripting. The following sections -are meant as guidelines and highlight possible pitfalls with both production and consumption of API -functionality. For clarity when speaking of "scripter" we are referring to those writing scripts to -be run by pyscript and "designer" as the `cmd2` application author. +If you as an app designer have not explicitly disabled the `run_pyscript` command, you should assume +your application will be used for higher-level Python scripting. The following sections are meant as +guidelines and highlight possible pitfalls with both production and consumption of API +functionality. For clarity, a "scripter" writes pyscripts, and a "designer" is the `cmd2` +application author. ### Basics @@ -76,10 +75,9 @@ workflows using simple `app` calls. The result of a `run_pyscript` app call yiel `data`. `stdout` and `stderr` are fairly straightforward representations of normal data streams and -accurately reflect what is seen by the user during normal cmd2 interaction. `stop` contains -information about how the invoked command has ended its lifecycle. Lastly `data` contains any -information the designer sets via `self.last_result` or `self._cmd.last_result` if called from -inside a CommandSet. +accurately reflect what the user sees during normal cmd2 interaction. `stop` contains information +about how the invoked command has ended its lifecycle. Lastly `data` contains any information the +designer sets via `self.last_result` or `self._cmd.last_result` if called from inside a CommandSet. Python scripts executed with [run_pyscript](./builtin_commands.md#run_pyscript) can run `cmd2` application commands by using the syntax: @@ -94,7 +92,7 @@ where: attribute - `command` and `args` are entered exactly like they would be entered by a user of your application. -Using fstrings tends to be the most straight forward and easily readable way to provide parameters.: +Using f-strings tends to be the most straightforward and easily readable way to provide parameters.: ```py first = 'first' @@ -112,11 +110,10 @@ script for more information. ### Design principles If your cmd2 application follows the -[unix_design_philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) a scripter will have the -most flexibility to piece together workflows using different commands. If the designer's application -is more complete and less likely to be augmented in the future a scripter may opt for simple serial -scripts with little control flow. In either case, choices made by the designer will have effects on -scripters. +[Unix design philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) a scripter will have the +most flexibility to create workflows using different commands. If the designer's application is more +complete and less likely to be augmented in the future a scripter can use simple serial scripts with +little control flow. In either case, choices made by the designer will have effects on scripters. The following diagram illustrates the different boundaries to keep in mind. @@ -140,18 +137,18 @@ flowchart LR !!! note - As a designer it is preferable to design from the inside out. Your code will be infinitely far easier to unit test than at the higher level. While there are regression testing extensions for cmd2, unit testing will always be faster for development. + As a designer, you should design from the inside out. Your code will be much easier to unit test than at the higher level. While there are regression testing extensions for cmd2, unit testing will always be faster for development. !!! warning - It is bad design for a high level pyscript to know about, let alone access, low level class libraries of an application. Resist this urge at all costs, unless it's necessary. + It is bad design for a high-level pyscript to know about, let alone access, low-level class libraries of an application. Resist this urge as much as possible, unless it's necessary. ### Developing a Basic API -`cmd2` out of the box allows scripters to take advantage of all exposed `do_*` commands. As a -scripter one can easily interact with the application via `stdout` and `stderr`. +By default, `cmd2` allows scripters to take advantage of all exposed `do_*` commands. As a scripter, +you can easily interact with the application via `stdout` and `stderr`. -As a baseline let's start off with the the following `cmd2` application called `FirstApp` +As a baseline, let's start with the following `cmd2` application called `FirstApp` ```py #!/usr/bin/env python @@ -198,7 +195,7 @@ if __name__ == '__main__': sys.exit(c.cmdloop()) ``` -Let's start off on the wrong foot: +Let's start with an example of what not to do: ```py app('speak' @@ -212,11 +209,11 @@ SyntaxError: unexpected EOF while parsing SyntaxError: unexpected EOF while parsing ``` -cmd2 pyscripts require **valid** python code as a first step. +`cmd2` pyscripts require **valid** Python code as a first step. !!! warning - It is a common misconception that all application exceptions will "bubble" up from below. Unfortunately or fortunately this is not the case. `cmd2` sinkholes all application exceptions and there are no means to handle them. + It is a common misconception that all application exceptions will propagate up from below. This is not the case. `cmd2` catches all application exceptions and there are no means to handle them. When executing the `speak` command without parameters you see the following error: @@ -224,8 +221,8 @@ When executing the `speak` command without parameters you see the following erro Usage: speak [-h] [-p] [-s] [-r REPEAT] words [...] Error: the following arguments are required: words -Even though this is a fully qualified `cmd2` error the pyscript must look for this error and perform -error checking.: +Even though this is a fully qualified `cmd2` error, the pyscript must check for this error and +perform error checking.: ```py app('speak') @@ -247,7 +244,7 @@ print(result) (Cmd) run_pyscript script.py CommandResult(stdout='', stderr='Usage: speak [-h] [-p] [-s] [-r REPEAT] words [...]\nError: the following arguments are required: words\n\n', stop=False, data=None) -Now we can see that there has been an error. Let's re write the script to perform error checking.: +Now we can see that there has been an error. Let's rewrite the script to perform error checking.: ```py result = app('speak') @@ -259,7 +256,7 @@ if not result: (Cmd) run_pyscript script.py Something went wrong -In Python development, it is good practice to fail and exit quickly after user input.: +In Python development, it is good practice to fail fast after user input.: ```py import sys @@ -276,8 +273,8 @@ print("Continuing along..") (Cmd) run_pyscript script.py Continuing along.. -We changed the input to be a valid `speak` command but no output. Again we must inspect the -`CommandResult`: +We changed the input to be a valid `speak` command, but there was no output. Again we must inspect +the `CommandResult`: ```py import sys @@ -294,18 +291,18 @@ print(result.stdout) (Cmd) run_pyscript script.py TRUTH!!! -By just using `stdout` and `stderr` it is possible to string together commands with rudimentary -control flow. In the next section we will show how to take advantage of `cmd_result` data. +By just using `stdout` and `stderr` it is possible to chain commands with rudimentary control flow. +In the next section we will show how to use `cmd_result` data. ### Developing an Advanced API -Until now the application designer has paid little attention to scripters and their needs. Wouldn't -it be nice if while creating pyscripts one did not have to parse data from `stdout`? We can -accommodate the weary scripter by adding one small line at the end of our `do_*` commands. +So far, we haven't focused on the scripter's needs. Wouldn't it be nice if while creating pyscripts +you did not have to parse data from `stdout`? We can accommodate the scripter by adding one small +line at the end of our `do_*` commands. `self.last_result = ` -Adding the above line supercharges a cmd2 application and opens a new world of possibilities. +Adding the above line enhances a cmd2 application and opens a new world of possibilities. !!! tip @@ -315,7 +312,7 @@ Adding the above line supercharges a cmd2 application and opens a new world of p self._cmd.last_result = ``` -In the following command example we return an array containing directory elements.: +In the following command example we return a list containing directory elements.: ```py dir_parser = cmd2.Cmd2ArgumentParser() @@ -353,22 +350,22 @@ Results: Cmd) run_pyscript script.py ['.venv', 'app.py', 'script.py'] -As a rule of thumb it is safer for the designer to return simple scalar types as command results -instead of complex objects. If there is benefit in providing class objects designers should choose -immutable over mutable types and never provide direct access to class members as this could -potentially lead to violation of the -[open_closed_principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). +As a rule of thumb, designers should return simple scalar types as command results instead of +complex objects. If it is beneficial in providing class objects designers should choose immutable +over mutable types and never provide direct access to class members as this could potentially lead +to violation of the +[open-closed_principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). -When possible, a frozen dataclass is a lightweight solution perfectly suited for data manipulation. -Let's dive into an example. +When possible, a frozen dataclass is a lightweight solution ideal for data manipulation. Let's look +at an example. -The following fictional application has two commands: `build` and `status`. We can pretend that the -build action happens somewhere else in the world at a REST API endpoint and has significant -computational cost. The status command for all intents and purposes, will only show the current -status of a build task. The application has provided all that is needed for a user to start a build -and then determine its status. The problem however is that with a long running process the user may -want to wait for it to finish. A designer may be tempted to create a command to start a build and -then poll for status until finished, but this scenario is better solved as an extensible script. +The following application has two commands: `build` and `status`. Let's assume that the build action +happens somewhere else in the world at a REST API endpoint and has significant computational cost. +The status command, will only show the current status of a build task. The application has provided +everything that is needed for a user to start a build and then determine its status. However, the +problem is that with a long running process the user may want to wait for it to finish. A designer +may be tempted to create a command to start a build and then poll for status until finished, but +this scenario is better solved as a script. app.py: @@ -446,7 +443,7 @@ if __name__ == "__main__": sys.exit(c.cmdloop()) ``` -The below is a possible solution via pyscript: +Below is a possible solution via pyscript: ```py import sys @@ -455,7 +452,7 @@ import time # start build result = app('build tower') -# If there was an error then quit now +# If there was an error then exit if not result: print('Build failed') sys.exit() @@ -465,7 +462,7 @@ build = result.data print(f"Build {build.name} : {build.status}") -# Poll status (it would be wise to NOT hang here) +# Poll status while True: # Perform status check @@ -478,7 +475,7 @@ while True: build_status = result.data - # If the status shows complete then we are done + # If the status shows complete then the script is done if build_status.status in ['finished', 'canceled']: print(f"Build {build.name} has completed") break diff --git a/docs/features/shortcuts_aliases_macros.md b/docs/features/shortcuts_aliases_macros.md index aff2b5efd..4e1aa8ec4 100644 --- a/docs/features/shortcuts_aliases_macros.md +++ b/docs/features/shortcuts_aliases_macros.md @@ -2,16 +2,16 @@ ## Shortcuts -Command shortcuts for long command names and common commands can make life more convenient for your -users. Shortcuts are used without a space separating them from their arguments, like `!ls`. By -default, the following shortcuts are defined: +Command shortcuts for long command names and common commands can be more convenient for your users. +Shortcuts are used without a space separating them from their arguments, like `!ls`. By default, the +following shortcuts are defined: - **`?`** - help - **`!`** - shell: run as OS-level command - **`@`** - run script file - **`@@`** - run script file; filename is relative to current script location -To define more shortcuts, start with the [cmd2.DEFAULT_SHORTCUTS][] constant which is a dictionary +To define more shortcuts, start with the [cmd2.DEFAULT_SHORTCUTS][] constant, which is a dictionary, and then add more shortcuts to it by updating it with a dictionary of additional shortcuts in the format `{'shortcut': 'command_name'}` where you omit `do_` from the command name: @@ -25,7 +25,9 @@ class App(Cmd): !!! warning - Shortcuts need to be created by updating the `shortcuts` dictionary attribute prior to calling the `cmd2.Cmd` super class `__init__()` method. Moreover, that super class init method needs to be called after updating the `shortcuts` attribute This warning applies in general to many other attributes which are not settable at runtime. + Shortcuts need to be created by updating the `shortcuts` dictionary attribute before calling the `cmd2.Cmd` super class `__init__()` method. Moreover, that super class init method needs to be called after updating the `shortcuts` attribute. + + This warning applies in general to many other attributes which are not settable at runtime. !!! tip @@ -33,8 +35,8 @@ class App(Cmd): ## Aliases -In addition to shortcuts, `cmd2` provides a full alias feature via the `alias` command. Aliases work -in a similar fashion to aliases in the Bash shell. +In addition to shortcuts, `cmd2` provides an alias feature via the `alias` command. Aliases work +like aliases in the Bash shell. The syntax to create an alias is: `alias create name command [args]`, e.g. `alias create ls !ls -lF`. @@ -44,13 +46,13 @@ from being redirected: alias create save_results print_results ">" out.txt -Tab completion recognizes an alias, and completes as if its actual value was on the command line. +Tab completion recognizes an alias, and completes as if the aliased command was on the command line. For more details run: `help alias create` Use `alias list` to see all or some of your aliases. The output of this command displays your -aliases using the same command that was used to create them. Therefore you can place this output in -a `cmd2` startup script to recreate your aliases each time you start the application +aliases in a format that can be used to create them. Therefore you can place this output in a `cmd2` +startup script to recreate your aliases each time you start the application > Ex: `alias list` @@ -72,8 +74,8 @@ The following creates a macro called `my_macro` that expects two arguments: macro create my_macro make_dinner -meat {1} -veggie {2} -When the macro is called, the provided arguments are resolved and the assembled command is run. For -example: +When the macro is called, the provided arguments are substituted and the assembled command is run. +For example: my_macro beef broccoli ---> make_dinner -meat beef -veggie broccoli From 31731aac75e14a5d727ec9bfbf02e64151e6f289 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 23:16:29 -0400 Subject: [PATCH 09/18] More minor working improvements --- docs/api/index.md | 6 +++--- docs/examples/alternate_event_loops.md | 18 +++++++++--------- docs/examples/getting_started.md | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/api/index.md b/docs/api/index.md index c52dca6f8..36789dc49 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -2,9 +2,9 @@ These pages document the public API for `cmd2`. If a method, class, function, attribute, or constant is not documented here, consider it private and subject to change. There are many classes, methods, -functions, and constants in the source code which do not begin with an underscore but are not +functions, and constants in the source code that do not begin with an underscore but are not documented here. When looking at the source code for this library, you cannot safely assume that -because something doesn't start with an underscore, it is a public API. +something is a public API just because it doesn't start with an underscore. If a release of this library changes any of the items documented here, the version number will be incremented according to the [Semantic Version Specification](https://semver.org). @@ -18,7 +18,7 @@ incremented according to the [Semantic Version Specification](https://semver.org - [cmd2.colors](./colors.md) - StrEnum of all color names supported by the Rich library - [cmd2.command_definition](./command_definition.md) - supports the definition of commands in separate classes to be composed into cmd2.Cmd -- [cmd2.constants](./constants.md) - just like it says on the tin +- [cmd2.constants](./constants.md) - constants used in `cmd2` - [cmd2.decorators](./decorators.md) - decorators for `cmd2` commands - [cmd2.exceptions](./exceptions.md) - custom `cmd2` exceptions - [cmd2.history](./history.md) - classes for storing the history of previously entered commands diff --git a/docs/examples/alternate_event_loops.md b/docs/examples/alternate_event_loops.md index c3a3916cc..3a6ebc9d8 100644 --- a/docs/examples/alternate_event_loops.md +++ b/docs/examples/alternate_event_loops.md @@ -1,9 +1,9 @@ # Alternate Event Loops -Throughout this documentation we have focused on the **90%** use case, that is the use case we -believe around **90+%** of our user base is looking for. This focuses on ease of use and the best -out-of-the-box experience where developers get the most functionality for the least amount of -effort. We are talking about running `cmd2` applications with the `cmdloop()` method: +Throughout this documentation, we have focused on the 90% use case, which we believe applies to over +90% of our user base. This focuses on ease of use and the best out-of-the-box experience, where +developers get the most functionality for the least amount of effort. We are talking about running +`cmd2` applications with the `cmdloop()` method: ```py from cmd2 import Cmd @@ -21,8 +21,8 @@ Many Python concurrency libraries involve or require an event loop which they ar such as [asyncio](https://docs.python.org/3/library/asyncio.html), [gevent](http://www.gevent.org/), [Twisted](https://twistedmatrix.com), etc. -`cmd2` applications can be executed in a fashion where `cmd2` doesn't own the main loop for the -program by using code like the following: +`cmd2` applications can be executed in a way where `cmd2` doesn't own the main loop for the program +by using code like the following: ```py import cmd2 @@ -64,9 +64,9 @@ normal fashion: 1. Stop redirecting output if it was redirected 1. Call methods registered with [cmd2.Cmd.register_cmdfinalization_hook][] -Running in this fashion enables the ability to integrate with an external event loop. However, how -to integrate with any specific event loop is beyond the scope of this documentation. Please note -that running in this fashion comes with several disadvantages, including: +Running in this way enables the ability to integrate with an external event loop. However, how to +integrate with any specific event loop is beyond the scope of this documentation. Please note that +running in this fashion comes with several disadvantages, including: - Requires the developer to write more code - Does not support transcript testing diff --git a/docs/examples/getting_started.md b/docs/examples/getting_started.md index e88d26974..2a3d2c5d8 100644 --- a/docs/examples/getting_started.md +++ b/docs/examples/getting_started.md @@ -1,6 +1,6 @@ # Getting Started -Here's a quick walkthrough of a the simple +Here's a quick walkthrough of the simple [getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) example application which demonstrates many features of `cmd2`: @@ -54,7 +54,7 @@ $ python getting_started.py The application creates an instance of our class, and calls the [cmd2.Cmd.cmdloop][] method. This method accepts user input and runs commands based on that input. Because we subclassed `cmd2.Cmd`, -our new app already has a bunch of features built in. +our new app already has a bunch of built-in features. Congratulations, you have a working `cmd2` app. You can run it, and then type `quit` to exit. @@ -87,7 +87,7 @@ you will see our `maxrepeats` setting show up with its default value of `3`. ## Create A Command -Now we will create our first command, called `speak` which will echo back whatever we tell it to +Now we will create our first command, called `speak`, which will echo back whatever we tell it to say. We are going to use an [argument processor](../features/argument_processing.md) so the `speak` command can shout and talk pig latin. We will also use some built in methods for [generating output](../features/generating_output.md). Add this code to `getting_started.py`, so From cfd1c442b78aa8993153c68f2ef32500854df0d3 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 23:19:05 -0400 Subject: [PATCH 10/18] Capitalize proper name --- docs/examples/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/getting_started.md b/docs/examples/getting_started.md index 2a3d2c5d8..4193aa4e2 100644 --- a/docs/examples/getting_started.md +++ b/docs/examples/getting_started.md @@ -89,7 +89,7 @@ you will see our `maxrepeats` setting show up with its default value of `3`. Now we will create our first command, called `speak`, which will echo back whatever we tell it to say. We are going to use an [argument processor](../features/argument_processing.md) so the `speak` -command can shout and talk pig latin. We will also use some built in methods for +command can shout and talk Pig Latin. We will also use some built in methods for [generating output](../features/generating_output.md). Add this code to `getting_started.py`, so that the `speak_parser` attribute and the `do_speak()` method are part of the `BasicApp()` class: From 35bf7ddbd2fe17f3a52c877ed66680a0581cf0a8 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 13 Sep 2025 23:24:41 -0400 Subject: [PATCH 11/18] Simplify some wording --- docs/examples/getting_started.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/examples/getting_started.md b/docs/examples/getting_started.md index 4193aa4e2..ef98327cc 100644 --- a/docs/examples/getting_started.md +++ b/docs/examples/getting_started.md @@ -124,7 +124,7 @@ import argparse There's a bit to unpack here, so let's walk through it. We created `speak_parser`, which uses the [argparse](https://docs.python.org/3/library/argparse.html) module from the Python standard library -to parse command line input from a user. There is nothing thus far that is specific to `cmd2`. +to parse command line input from a user. So far, there is nothing specific to `cmd2`. There is also a new method called `do_speak()`. In both [cmd](https://docs.python.org/3/library/cmd.html) and `cmd2`, methods that start with `do_` become @@ -137,12 +137,12 @@ Note the `cmd2.decorators.with_argparser` decorator on the `do_speak()` method. the user input doesn't meet the requirements defined by the argparser, then an error will be displayed for the user. 1. It alters our `do_speak` method so that instead of receiving the raw user input as a parameter, - we receive the namespace from the argparser. + we receive the namespace from the argument parser. 1. It creates a help message for us based on the argparser. You can see in the body of the method how we use the namespace from the argparser (passed in as the -variable `args`). We build an array of words which we will output, honoring both the `--piglatin` -and `--shout` options. +variable `args`). We build a list of words which we will output, honoring both the `--piglatin` and +`--shout` options. At the end of the method, we use our `maxrepeats` setting as an upper limit to the number of times we will print the output. @@ -198,9 +198,9 @@ def __init__(self): Shortcuts are passed to the `cmd2` initializer, and if you want the built-in shortcuts of `cmd2` you have to pass them. These shortcuts are defined as a dictionary, with the key being the shortcut, and -the value containing the command. When using the default shortcuts and also adding your own, it's a -good idea to use the `.update()` method to modify the dictionary. This way if you add a shortcut -that happens to already be in the default set, yours will override, and you won't get any errors at +the value containing the command. When using the default shortcuts and adding your own, it's a good +idea to use the `.update()` method to modify the dictionary. This way, if you add a shortcut that +happens to already be in the default set, yours will override, and you won't get any errors at runtime. Run your app again, and type: @@ -209,16 +209,15 @@ Run your app again, and type: (Cmd) shortcuts ``` -to see the list of all of the shortcuts, including the one for speak that we just created. +to see the list of all the shortcuts, including the one for speak that we just created. ## Multiline Commands -Some use cases benefit from the ability to have commands that span more than one line. For example, -you might want the ability for your user to type in a SQL command, which can often span lines and -which are terminated with a semicolon. Let's add a -[multiline command](../features/multiline_commands.md) to our application. First we'll create a new -command called `orate`. This code shows both the definition of our `speak` command, and the `orate` -command: +Some use cases benefit from commands that span more than one line. For example, you might want the +ability for your user to type in a SQL command, which can often span lines and which are terminated +with a semicolon. Let's add a [multiline command](../features/multiline_commands.md) to our +application. First we'll create a new command called `orate`. This code shows both the definition of +our `speak` command, and the `orate` command: ```py @cmd2.with_argparser(speak_parser) From 0298bd310470c113cfd45f0d1a208ac175e2127c Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sun, 14 Sep 2025 15:17:22 -0400 Subject: [PATCH 12/18] Added a new documentation section on `cmd2 Major Version Upgrades` and put in a draft for the 2.x -> 3.x upgrade info --- docs/features/builtin_commands.md | 4 +- docs/features/prompt.md | 13 ++-- docs/upgrades.md | 108 ++++++++++++++++++++++++++++++ mkdocs.yml | 2 + 4 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 docs/upgrades.md diff --git a/docs/features/builtin_commands.md b/docs/features/builtin_commands.md index 5cefb35a1..f2bc71820 100644 --- a/docs/features/builtin_commands.md +++ b/docs/features/builtin_commands.md @@ -63,9 +63,9 @@ This command runs a Python script file inside the `cmd2` application. See This command runs commands in a script file that is encoded as either ASCII or UTF-8 text. See [Command Scripts](./scripting.md#command-scripts) for more information. -### \_relative_run_script (hidden) +### \_relative_run_script -This command is hidden from the help that's visible to end users. It runs a script like +**This command is hidden from the help that's visible to end users.** It runs a script like [run_script](#run_script) but does so using a path relative to the script that is currently executing. This is useful when you have scripts that run other scripts. See [Running Command Scripts](../features/scripting.md#running-command-scripts) for more information. diff --git a/docs/features/prompt.md b/docs/features/prompt.md index a08d6b6c4..2ff3ae0d4 100644 --- a/docs/features/prompt.md +++ b/docs/features/prompt.md @@ -34,19 +34,16 @@ text at the prompt. To use this functionality, the application must be running i supports [VT100](https://en.wikipedia.org/wiki/VT100) control characters and `readline`. Linux, Mac, and Windows 10 and greater all support these. -::: cmd2.Cmd.async_alert - -::: cmd2.Cmd.async_update_prompt - -::: cmd2.Cmd.async_refresh_prompt - -::: cmd2.Cmd.need_prompt_refresh +- [cmd2.Cmd.async_alert][] +- [cmd2.Cmd.async_update_prompt][] +- [cmd2.Cmd.async_refresh_prompt][] +- [cmd2.Cmd.need_prompt_refresh][] `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. -::: cmd2.Cmd.set_window_title +- [cmd2.Cmd.set_window_title][] The easiest way to understand these functions is to see the [async_printing.py](https://github.com/python-cmd2/cmd2/blob/main/examples/async_printing.py) diff --git a/docs/upgrades.md b/docs/upgrades.md new file mode 100644 index 000000000..e5f368c3c --- /dev/null +++ b/docs/upgrades.md @@ -0,0 +1,108 @@ +# cmd2 Major Versions Upgrades + +## Upgrading to cmd2 3.x from 2.x + +For details about all of the changes in the 3.0.0 release, please refer to +[CHANGELOG.md](https://github.com/python-cmd2/cmd2/blob/main/CHANGELOG.md). + +The biggest change from 2.x to 3.x is that `cmd2` now has a dependency on +[rich](https://github.com/Textualize/rich). Accordingly, `cmd2` now relies on `rich` for beautiful +text styling and formatting within the terminal. As such, a good chunk of custom code has been +removed from `cmd2` and other things have either moved or altered to be based on `rich`. + +The major things users should be aware of when upgrading to 3.x are detailed in subsections below. + +### Deleted Modules + +#### ansi + +The functionality within the `cmd2.ansi` module has either been removed or changed to be based on +`rich` and moved to one of the new modules: [cmd2.string_utils][], [cmd2.styles][], or +[cmd2.terminal_utils][]. + +#### table_creator + +The `cmd2.table_creator` module no longer exists. Please see rich's documentation on +[Tables](https://rich.readthedocs.io/en/latest/tables.html) for more information. The +[rich_tables.py](https://github.com/python-cmd2/cmd2/blob/main/examples/rich_tables.py) example +demonstrates how to use `rich` tables in a `cmd2` application. + +`rich` tables offer a degree of power and flexibility that are superior to what `cmd2` previously +offered. We apologize for this backwards incompatibility, but the APIs were fundamentally different +and we could not figure out a way to create a backwards-compatibility wrapper. + +### Added modules + +#### colors + +The new [cmd2.colors][] module provides the convenient [cmd2.colors.Color][] `StrEnum` class for +`rich` color names. This allows you to use tab-completable constants in your code instead of magic +strings to represent the precise color you want. + +See the +[getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) for +a basic example of using the `Color` class to choose colors for stylizing your output. +Alternatively, see the [color.py](https://github.com/python-cmd2/cmd2/blob/main/examples/color.py) +example for a visual demonstration of all supported colors. + +#### rich_utils + +The new [cmd2.rich_utils][] module provides common utility classes and functions for supporting the +use of `rich` within `cmd2` applications. Most of what is here is not intended to be user-facing. + +The one thing many `cmd2` application developers will likely be interested in using is the +[cmd2.rich_utils.set_theme][] function. See the +[rich_theme.py](https://github.com/python-cmd2/cmd2/blob/main/examples/rich_theme.py) example for a +demonstration for how to set a theme (color scheme) for your app. + +#### styles + +Default styles for how something like an error message should be displayed are now located in the +new [cmd2.styles][] module and they are now based on `rich` styles. + +Previously `cmd2` default styles were in the `cmd2.ansi` module. + +See +[argparse_completion.py](https://github.com/python-cmd2/cmd2/blob/main/examples/argparse_completion.py) +for an example on how you can leverage these default styles in your `cmd2` application to maintain a +consistent look in feel. + +#### string_utils + +Various string utility functions have been moved from the `cmd2.ansi` module to the new +[cmd2.string_utils][] module. + +This includes functions for styling, aligning, and quoting/un-quoting text. See the +[getting_started.py](https://github.com/python-cmd2/cmd2/blob/main/examples/getting_started.py) +example for a demonstration of how to use the common [cmd2.string_utils.stylize][] function. + +#### terminal_utils + +Support for terminal control escape sequences for things like setting the window title and +asynchronous alerts has been moved from `cmd2.ansi` to the new [cmd2.terminal_utils][] module. + +This isn't really intended to be used by end users, but is used by higher-level functionality that +is intended to be used by end users such as [cmd2.Cmd.set_window_title][] and +[cmd2.Cmd.async_alert][]. + +See [async_printing.py](https://github.com/python-cmd2/cmd2/blob/main/examples/async_printing.py) +for an example of how to use this functionality in a `cmd2` application. + +### Argparse HelpFormatter classes + +`cmd2` now has 5 different Argparse HelpFormatter classes, all of which are based on the +`RichHelpFormatter` class from [rich-argparse](https://github.com/hamdanal/rich-argparse). + +- [Cmd2HelpFormatter][cmd2.argparse_custom.Cmd2HelpFormatter] +- [ArgumentDefaultsCmd2HelpFormatter][cmd2.argparse_custom.ArgumentDefaultsCmd2HelpFormatter] +- [MetavarTypeCmd2HelpFormatter][cmd2.argparse_custom.MetavarTypeCmd2HelpFormatter] +- [RawDescriptionCmd2HelpFormatter][cmd2.argparse_custom.RawDescriptionCmd2HelpFormatter] +- [RawTextCmd2HelpFormatter][cmd2.argparse_custom.RawTextCmd2HelpFormatter] + +Previously the default `Cmd2HelpFormatter` class inherited from `argparse.RawTextHelpFormatter`, +however it now inherits from `argparse.HelpFormatter`. If you want RawText behavior, then pass +`formatter_class=RawTextCmd2HelpFormatter` to your parser. + +The benefit is that your `cmd2` applications now have more aesthetically pleasing help which +includes color to make it quicker and easier to visually parse help text. This works for all +supported versions of Python. diff --git a/mkdocs.yml b/mkdocs.yml index df42da4a6..944c77900 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -219,6 +219,8 @@ nav: - api/terminal_utils.md - api/transcript.md - api/utils.md + - Version Upgrades: + - upgrades.md - Meta: - doc_conventions.md From a71164027b7013e4bd496b385f18bb2734d0e14d Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sun, 14 Sep 2025 15:26:29 -0400 Subject: [PATCH 13/18] Added new feature doc on configuring a custom theme --- docs/features/index.md | 1 + docs/features/theme.md | 10 ++++++++++ mkdocs.yml | 1 + 3 files changed, 12 insertions(+) create mode 100644 docs/features/theme.md diff --git a/docs/features/index.md b/docs/features/index.md index 13f99715b..9cbf65072 100644 --- a/docs/features/index.md +++ b/docs/features/index.md @@ -27,6 +27,7 @@ - [Shortcuts, Aliases, and Macros](shortcuts_aliases_macros.md) - [Startup Commands](startup_commands.md) - [Table Creation](table_creation.md) +- [Theme](theme.md) - [Transcripts](transcripts.md) diff --git a/docs/features/theme.md b/docs/features/theme.md new file mode 100644 index 000000000..791161bf7 --- /dev/null +++ b/docs/features/theme.md @@ -0,0 +1,10 @@ +# Theme + +`cmd2` provides the ability to configure an overall theme for your application using the +[cmd2.rich_utils.set_theme][] function. This is based o the +[rich.theme](https://rich.readthedocs.io/en/stable/reference/theme.html) container for style +information. You can use this to brand your application and set an overall consistent look and feel +that is appealing to your user base. + +See [rich_theme.py](https://github.com/python-cmd2/cmd2/blob/main/examples/rich_theme.py) for a +simple example of configuring a custom theme for your `cmd2` application. diff --git a/mkdocs.yml b/mkdocs.yml index 944c77900..8712bbbfd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -186,6 +186,7 @@ nav: - features/shortcuts_aliases_macros.md - features/startup_commands.md - features/table_creation.md + - features/theme.md - features/transcripts.md - Examples: - examples/index.md From 5168187c4c482ae9f12815f9e63611f648e23944 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sun, 14 Sep 2025 16:29:43 -0400 Subject: [PATCH 14/18] A few very minor grammar fixes --- docs/features/argument_processing.md | 4 ++-- docs/features/commands.md | 12 ++++++------ docs/features/completion.md | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index 97eb64ba8..1cd66871a 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -227,8 +227,8 @@ class CmdLineApp(cmd2.Cmd): ## Unknown Positional Arguments -To pass all unknown arguments to be passed to your command as a list of strings, then decorate the -command method with the `@with_argparser(..., with_unknown_args=True)` decorator. +To pass all unknown arguments to your command as a list of strings, then decorate the command method +with the `@with_argparser(..., with_unknown_args=True)` decorator. Here's what it looks like: diff --git a/docs/features/commands.md b/docs/features/commands.md index acf538899..21a7864a9 100644 --- a/docs/features/commands.md +++ b/docs/features/commands.md @@ -73,34 +73,34 @@ already parsed. A `Statement` object is a subclass of `str` that contains the following attributes: -command +**command** : Name of the command called. You already know this because of the method `cmd2` called, but it can sometimes be nice to have it in a string, i.e. if you want your error messages to contain the command name. -args +**args** : A string containing the arguments to the command with output redirection or piping to shell commands removed. It turns out that the "string" value of the `Statement` object has all the output redirection and piping clauses removed as well. Quotes remain in the string. -command_and_args +**command_and_args** : A string of just the command and the arguments, with output redirection or piping to shell commands removed. -argv +**argv** : A list of arguments a-la `sys.argv`, including the command as `argv[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 +**raw** : Full input exactly as typed by the user. -terminator +**terminator** : Character used to end a multiline command. You can configure multiple termination characters, and this attribute will tell you which one the user typed. diff --git a/docs/features/completion.md b/docs/features/completion.md index 014dcb810..36e8a8f48 100644 --- a/docs/features/completion.md +++ b/docs/features/completion.md @@ -84,7 +84,7 @@ an error occurs in which it is more desirable to display a message than a stack has a member called `apply_style`. Set this False if the error style should not be applied. For instance, `ArgparseCompleter` sets it to False when displaying completion hints. -## Tab Completion Using argparse Decorator {: #argparse-based } +## Tab Completion Using the argparse Decorator {: #argparse-based } When using `cmd2`'s [@with_argparser][cmd2.with_argparser] decorator, `cmd2` provides automatic tab completion of flag names. From 39166967ed706f576c372171b9499f10ea78124f Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Mon, 15 Sep 2025 18:52:37 -0400 Subject: [PATCH 15/18] Grammar fix based on feedback --- docs/upgrades.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/upgrades.md b/docs/upgrades.md index e5f368c3c..1ae783a83 100644 --- a/docs/upgrades.md +++ b/docs/upgrades.md @@ -65,7 +65,7 @@ Previously `cmd2` default styles were in the `cmd2.ansi` module. See [argparse_completion.py](https://github.com/python-cmd2/cmd2/blob/main/examples/argparse_completion.py) for an example on how you can leverage these default styles in your `cmd2` application to maintain a -consistent look in feel. +consistent look and feel. #### string_utils From 90f72d3e2c0d3c297e790a8051dea2fbd251d2ce Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Mon, 15 Sep 2025 19:21:23 -0400 Subject: [PATCH 16/18] Added info on description and epliog fields within argument parsers being rich objects and info on HelpFormatter classes --- docs/features/argument_processing.md | 33 +++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index 1cd66871a..12a2b359c 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -80,6 +80,12 @@ By default, `cmd2` uses the docstring of the command method when a user asks for command. When you use the `@with_argparser` decorator, the docstring for the `do_*` method is used to set the description for the `argparse.ArgumentParser`. +!!! tip "description and epilog fields are rich objects" + + While the `help` text itself is simply a string, both the `description` and `epilog` can contain + [rich](https://github.com/Textualize/rich) objects. For the `description` and `epilog` fields, you can pass + in any `rich` object, including Text, Tables, Markdown. + With this code: ```py @@ -174,7 +180,32 @@ This command cannot generate tags with no content, like
!!! warning - If a command **foo** is decorated with `cmd2`'s `with_argparse` decorator, then **help_foo** will not be invoked when `help foo` is called. The [argparse](https://docs.python.org/3/library/argparse.html) module provides a rich API which can be used to tweak every aspect of the displayed help and we encourage `cmd2` developers to utilize that. + If a command **foo** is decorated with `cmd2`'s `with_argparse` decorator, then **help_foo** will not be + invoked when `help foo` is called. The [argparse](https://docs.python.org/3/library/argparse.html) module + provides a rich API which can be used to tweak every aspect of the displayed help and we encourage `cmd2` + developers to utilize that. + +### Argparse HelpFormatter classes + +`cmd2` has 5 different Argparse HelpFormatter classes, all of which are based on the +`RichHelpFormatter` class from [rich-argparse](https://github.com/hamdanal/rich-argparse). The +benefit is that your `cmd2` applications now have more aesthetically pleasing help which includes +color to make it quicker and easier to visually parse help text. This works for all supported +versions of Python. + +- [Cmd2HelpFormatter][cmd2.argparse_custom.Cmd2HelpFormatter] - default help formatter class +- [ArgumentDefaultsCmd2HelpFormatter][cmd2.argparse_custom.ArgumentDefaultsCmd2HelpFormatter] - adds + default values to argument help +- [MetavarTypeCmd2HelpFormatter][cmd2.argparse_custom.MetavarTypeCmd2HelpFormatter] - uses the + argument 'type' as the default metavar value (instead of the argument 'dest') +- [RawDescriptionCmd2HelpFormatter][cmd2.argparse_custom.RawDescriptionCmd2HelpFormatter] - retains + any formatting in descriptions and epilogs +- [RawTextCmd2HelpFormatter][cmd2.argparse_custom.RawTextCmd2HelpFormatter] - retains formatting of + all help text + +The default `Cmd2HelpFormatter` class inherits from `argparse.HelpFormatter`. If you want a +different behavior, then pass the desired class to the `formatter_class` argument of your argparse +parser, e.g. `formatter_class=ArgumentDefaultsCmd2HelpFormatter` to your parser. ## Argument List From f6a313e4957be239fd40cfde728d409c8d03b7e8 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Mon, 15 Sep 2025 19:59:57 -0400 Subject: [PATCH 17/18] Deleted ignored ruff rule that no longer exists from ext-test pyproject.toml --- plugins/ext_test/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/ext_test/pyproject.toml b/plugins/ext_test/pyproject.toml index f46d152bd..b4e48324a 100644 --- a/plugins/ext_test/pyproject.toml +++ b/plugins/ext_test/pyproject.toml @@ -133,7 +133,6 @@ ignore = [ "S101", # Use of `assert` detected -- DO NOT FIX "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME "SLF001", # Private member accessed: `_Iterator` -- FIX ME - "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX ] # Allow fix for all enabled rules (when `--fix`) is provided. From 1cb3550c6ab228d864da41d05dea56d4d8bb6288 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Tue, 16 Sep 2025 11:19:07 -0400 Subject: [PATCH 18/18] Addressed PR comments --- docs/features/argument_processing.md | 35 +++++++++++++++++------ docs/features/disable_commands.md | 2 +- docs/features/generating_output.md | 32 +++++++++++++++++++-- docs/features/scripting.md | 5 ++-- docs/features/shortcuts_aliases_macros.md | 8 ++++-- docs/features/theme.md | 2 +- docs/migrating/minimum.md | 2 +- docs/overview/integrating.md | 2 +- 8 files changed, 68 insertions(+), 20 deletions(-) diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index 12a2b359c..4c9bf4629 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -34,13 +34,32 @@ All of these decorators accept an optional **preserve_quotes** argument which de Setting this argument to `True` is useful for cases where you are passing the arguments to another command which might have its own argument parsing. +## with_argparser decorator + +The [@with_argparser][cmd2.with_argparser] decorator can accept the following for its first +argument: + +1. An existing instance of `argparse.ArgumentParser` +2. A function or static method which returns an instance of `argparse.ArgumentParser` +3. Cmd or CommandSet class method which returns an instance of `argparse.ArgumentParser` + +In all cases the `@with_argparser` decorator creates a deep copy of the parser instance which it +stores internally. A consequence is that parsers don't need to be unique across commands. + +!!! warning + + Since the `@with_argparser` decorator is making a deep-copy of the parser provided, if you wish + to dynamically modify this parser at a later time, you need to retrieve this deep copy. This can + be done using `self._command_parsers.get(self.do_commandname)`. + ## Argument Parsing -For each command in the `cmd2.Cmd` subclass which requires argument parsing, create a unique -instance of `argparse.ArgumentParser()` which can parse the input appropriately for the command. -Then decorate the command method with the `@with_argparser` decorator, passing the argument parser -as the first parameter to the decorator. This changes the second argument of the command method, -which will contain the results of `ArgumentParser.parse_args()`. +For each command in the `cmd2.Cmd` subclass which requires argument parsing, create an instance of +`argparse.ArgumentParser()` which can parse the input appropriately for the command (or provide a +function/method that returns such a parser). Then decorate the command method with the +`@with_argparser` decorator, passing the argument parser as the first parameter to the decorator. +This changes the second argument of the command method, which will contain the results of +`ArgumentParser.parse_args()`. Here's what it looks like: @@ -82,9 +101,9 @@ to set the description for the `argparse.ArgumentParser`. !!! tip "description and epilog fields are rich objects" - While the `help` text itself is simply a string, both the `description` and `epilog` can contain - [rich](https://github.com/Textualize/rich) objects. For the `description` and `epilog` fields, you can pass - in any `rich` object, including Text, Tables, Markdown. + While the `help` field is simply a string, both the `description` and `epilog` fields can accept any + [rich](https://github.com/Textualize/rich) renderable. This allows you to include all of rich's + built-in objects like `Text`, `Table`, and `Markdown`. With this code: diff --git a/docs/features/disable_commands.md b/docs/features/disable_commands.md index d934de497..1a22f6541 100644 --- a/docs/features/disable_commands.md +++ b/docs/features/disable_commands.md @@ -11,7 +11,7 @@ See for and example of removing or hiding built-in commands. See [command_sets.py](https://github.com/python-cmd2/cmd2/blob/main/examples/command_sets.py) for an -example of dynamically enabling and dis-abling custom commands at runtime. +example of dynamically enabling and disabling custom commands at runtime. ## Remove A Command diff --git a/docs/features/generating_output.md b/docs/features/generating_output.md index db2c29b67..16b42ca7f 100644 --- a/docs/features/generating_output.md +++ b/docs/features/generating_output.md @@ -19,10 +19,31 @@ output you generate must be sent to `self.stdout`. You can use the methods descr everything will work fine. [cmd2.Cmd][] also includes a number of output related methods which you may use to enhance the output your application produces. -Since `cmd2` has a dependency on the [rich](https://github.com/Textualize/rich) library, all of -`cmd2`'s output methods can natively render `rich` +Since `cmd2` has a dependency on the [rich](https://github.com/Textualize/rich) library, the +following [cmd2.Cmd][] output methods can natively render `rich` [renderable objects](https://rich.readthedocs.io/en/latest/protocol.html), enabling beautiful and -complex output. +complex output: + +- [poutput][cmd2.Cmd.poutput] +- [perror][cmd2.Cmd.perror] +- [psuccess][cmd2.Cmd.psuccess] +- [pwarning][cmd2.Cmd.pwarning] +- [pfeedback][cmd2.Cmd.pfeedback] +- [ppaged][cmd2.Cmd.ppaged] + +!!! tip "Advanced output customization" + + Each of the above methods accepts additional optional parameters that help control how the output is + formatted: + + - `sep`: string to write between printed text. Defaults to " " + - `end`: string to write at end of printed text. Defaults to a newline + - `style`: optional style to apply to output + - `soft_wrap`: Enable soft wrap mode. If True, lines of text will not be word-wrapped or cropped to fit the terminal width. Defaults to True + - `emoji`: If True, Rich will replace emoji codes (e.g., 😃) with their corresponding Unicode characters. Defaults to False + - `markup`: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold]) as styled output. Defaults to False + - `highlight`: If True, Rich will automatically apply highlighting to elements within strings, such as common Python data types like numbers, booleans, or None. + - `rich_print_kwargs`: optional additional keyword arguments to pass to Rich's `Console.print()` ## Ordinary Output @@ -140,6 +161,11 @@ with display widths greater than 1. Additionally, ANSI style sequences are safel count toward the display width. This means colored text is supported. If text has line breaks, then each line is aligned independently. +!!! tip "Advanced alignment customization" + + You can also control output alignment using the `rich_print_kwargs.justify` member when calling + `cmd2`'s print methods. + ## Columnar Output When generating output in multiple columns, you often need to calculate the width of each item so diff --git a/docs/features/scripting.md b/docs/features/scripting.md index c05789f7f..429fba210 100644 --- a/docs/features/scripting.md +++ b/docs/features/scripting.md @@ -112,8 +112,9 @@ script for more information. If your cmd2 application follows the [Unix design philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) a scripter will have the most flexibility to create workflows using different commands. If the designer's application is more -complete and less likely to be augmented in the future a scripter can use simple serial scripts with -little control flow. In either case, choices made by the designer will have effects on scripters. +complete and less likely to be augmented in the future, a scripter can use simple serial scripts +with little control flow. In either case, choices made by the designer will have effects on +scripters. The following diagram illustrates the different boundaries to keep in mind. diff --git a/docs/features/shortcuts_aliases_macros.md b/docs/features/shortcuts_aliases_macros.md index 4e1aa8ec4..cd14dce29 100644 --- a/docs/features/shortcuts_aliases_macros.md +++ b/docs/features/shortcuts_aliases_macros.md @@ -29,7 +29,7 @@ class App(Cmd): This warning applies in general to many other attributes which are not settable at runtime. -!!! tip +!!! note Command, alias, and macro names cannot start with a shortcut @@ -62,7 +62,9 @@ Use `alias delete` to remove aliases For more details run: `help alias delete` -Note: Aliases cannot have the same name as a command or macro +!!! note + + Aliases cannot have the same name as a command or macro ## Macros @@ -99,6 +101,6 @@ For more details on listing macros run: `help macro list` For more details on deleting macros run: `help macro delete` -!!! warning +!!! note Macros cannot have the same name as a command or alias diff --git a/docs/features/theme.md b/docs/features/theme.md index 791161bf7..064178fa7 100644 --- a/docs/features/theme.md +++ b/docs/features/theme.md @@ -1,7 +1,7 @@ # Theme `cmd2` provides the ability to configure an overall theme for your application using the -[cmd2.rich_utils.set_theme][] function. This is based o the +[cmd2.rich_utils.set_theme][] function. This is based on the [rich.theme](https://rich.readthedocs.io/en/stable/reference/theme.html) container for style information. You can use this to brand your application and set an overall consistent look and feel that is appealing to your user base. diff --git a/docs/migrating/minimum.md b/docs/migrating/minimum.md index 9eb8532d3..6dfb6b269 100644 --- a/docs/migrating/minimum.md +++ b/docs/migrating/minimum.md @@ -43,6 +43,6 @@ application, you may be able to remove them. See [Exiting](../features/misc.md#e If you are distributing your application, you'll also need to ensure that `cmd2` is properly installed. You will need to add the following dependency to your `pyproject.toml` or `setup.py`: - 'cmd2>=2.7' + 'cmd2>=3,<4' See [Integrate cmd2 Into Your Project](../overview/integrating.md) for more details. diff --git a/docs/overview/integrating.md b/docs/overview/integrating.md index 3ac5ca3d4..b119deb86 100644 --- a/docs/overview/integrating.md +++ b/docs/overview/integrating.md @@ -3,7 +3,7 @@ Once installed, you will want to ensure that your project's dependencies include `cmd2`. Make sure your `pyproject.toml` or `setup.py` includes the following dependency - 'cmd2>=2.7' + 'cmd2>=3,<4' The `cmd2` project uses :simple-semver: [Semantic Versioning](https://semver.org), which means that any incompatible API changes will be release with a new major version number. The public API is