summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/ansi.py32
-rw-r--r--cmd2/argparse_custom.py315
-rw-r--r--cmd2/cmd2.py98
-rw-r--r--cmd2/constants.py10
-rw-r--r--cmd2/decorators.py55
-rw-r--r--cmd2/history.py42
-rwxr-xr-xcmd2/parsing.py186
-rw-r--r--cmd2/plugin.py4
-rw-r--r--cmd2/py_bridge.py42
-rw-r--r--cmd2/utils.py14
-rw-r--r--docs/api/argparse_completer.rst5
-rw-r--r--docs/api/argparse_custom.rst5
-rw-r--r--docs/api/cmd.rst35
-rw-r--r--docs/api/constants.rst29
-rw-r--r--docs/api/decorators.rst13
-rw-r--r--docs/api/history.rst17
-rw-r--r--docs/api/index.rst23
-rw-r--r--docs/api/parsing.rst75
-rw-r--r--docs/api/plugin.rst45
-rw-r--r--docs/api/py_bridge.rst5
-rw-r--r--docs/api/utility_classes.rst16
-rw-r--r--docs/api/utility_functions.rst54
-rw-r--r--docs/api/utils.rst78
-rw-r--r--docs/conf.py5
-rw-r--r--docs/doc_conventions.rst3
-rw-r--r--docs/examples/first_app.rst27
-rw-r--r--docs/features/argument_processing.rst8
-rw-r--r--docs/features/completion.rst13
-rw-r--r--docs/features/hooks.rst219
-rw-r--r--docs/features/plugins.rst72
-rw-r--r--docs/features/transcripts.rst7
-rw-r--r--docs/overview/integrating.rst7
32 files changed, 990 insertions, 569 deletions
diff --git a/cmd2/ansi.py b/cmd2/ansi.py
index eac2bd3a..27c9e87a 100644
--- a/cmd2/ansi.py
+++ b/cmd2/ansi.py
@@ -17,11 +17,33 @@ colorama.init(strip=False)
# Values for allow_style setting
STYLE_NEVER = 'Never'
+"""
+Constant for ``cmd2.ansi.allow_style`` to indicate ANSI sequences
+should be removed from all output.
+"""
STYLE_TERMINAL = 'Terminal'
+"""
+Constant for ``cmd2.ansi.allow_style`` to indicate ANSI sequences
+should be removed if the output is not going to the terminal.
+"""
STYLE_ALWAYS = 'Always'
+"""
+Constant for ``cmd2.ansi.allow_style`` to indicate ANSI sequences
+should alwyas be output.
+"""
# Controls when ANSI style style sequences are allowed in output
allow_style = STYLE_TERMINAL
+"""When using outside of a cmd2 app, set this variable to one of:
+
+- ``STYLE_NEVER`` - remove ANSI sequences from all output
+- ``STYLE_TERMINAL`` - remove ANSI sequences if the output is not going to the terminal
+- ``STYLE_ALWAYS`` - always output ANSI sequences
+
+to control the output of ANSI sequences by methods in this module.
+
+The default is ``STYLE_TERMINAL``.
+"""
# Regular expression to match ANSI style sequences (including 8-bit and 24-bit colors)
ANSI_STYLE_RE = re.compile(r'\x1b\[[^m]*m')
@@ -32,7 +54,9 @@ class ColorBase(Enum):
Base class used for defining color enums. See fg and bg classes for examples.
Child classes should define enums in the follow structure:
+
key: color name (e.g. black)
+
value: anything that when cast to a string returns an ANSI sequence
"""
def __str__(self) -> str:
@@ -111,17 +135,25 @@ class bg(ColorBase):
FG_RESET = fg.reset.value
+"""ANSI sequence to reset the foreground attributes"""
BG_RESET = bg.reset.value
+"""ANSI sequence to reset the terminal background attributes"""
RESET_ALL = Style.RESET_ALL
+"""ANSI sequence to reset all terminal attributes"""
# Text intensities
INTENSITY_BRIGHT = Style.BRIGHT
+"""ANSI sequence to make the text bright"""
INTENSITY_DIM = Style.DIM
+"""ANSI sequence to make the text dim"""
INTENSITY_NORMAL = Style.NORMAL
+"""ANSI sequence to make the text normal"""
# ANSI style sequences not provided by colorama
UNDERLINE_ENABLE = colorama.ansi.code_to_chars(4)
+"""ANSI sequence to turn on underline"""
UNDERLINE_DISABLE = colorama.ansi.code_to_chars(24)
+"""ANSI sequence to turn off underline"""
def strip_style(text: str) -> str:
diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py
index 81fec013..02e23f17 100644
--- a/cmd2/argparse_custom.py
+++ b/cmd2/argparse_custom.py
@@ -1,127 +1,150 @@
# coding=utf-8
"""
-This module adds capabilities to argparse by patching a few of its functions. It also defines a parser
-class called Cmd2ArgumentParser which improves error and help output over normal argparse. All cmd2 code uses
-this parser and it is recommended that developers of cmd2-based apps either use it or write their own parser
-that inherits from it. This will give a consistent look-and-feel between the help/error output of built-in
-cmd2 commands and the app-specific commands. If you wish to override the parser used by cmd2's built-in
-commands, see override_parser.py example.
+This module adds capabilities to argparse by patching a few of its functions.
+It also defines a parser class called Cmd2ArgumentParser which improves error
+and help output over normal argparse. All cmd2 code uses this parser and it is
+recommended that developers of cmd2-based apps either use it or write their own
+parser that inherits from it. This will give a consistent look-and-feel between
+the help/error output of built-in cmd2 commands and the app-specific commands.
+If you wish to override the parser used by cmd2's built-in commands, see
+override_parser.py example.
-Since the new capabilities are added by patching at the argparse API level, they are available whether or not
-Cmd2ArgumentParser is used. However, the help and error output of Cmd2ArgumentParser is customized to notate
-nargs ranges whereas any other parser class won't be as explicit in their output.
+Since the new capabilities are added by patching at the argparse API level,
+they are available whether or not Cmd2ArgumentParser is used. However, the help
+and error output of Cmd2ArgumentParser is customized to notate nargs ranges
+whereas any other parser class won't be as explicit in their output.
-############################################################################################################
-# Added capabilities
-############################################################################################################
-Extends argparse nargs functionality by allowing tuples which specify a range (min, max). To specify a max
-value with no upper bound, use a 1-item tuple (min,)
-
- Example:
- # -f argument expects at least 3 values
- parser.add_argument('-f', nargs=(3,))
-
- # -f argument expects 3 to 5 values
- parser.add_argument('-f', nargs=(3, 5))
-
-Tab Completion:
- cmd2 uses its ArgparseCompleter class to enable argparse-based tab completion on all commands that use the
- @with_argparse wrappers. Out of the box you get tab completion of commands, subcommands, and flag names,
- as well as instructive hints about the current argument that print when tab is pressed. In addition,
- you can add tab completion for each argument's values using parameters passed to add_argument().
-
- Below are the 5 add_argument() parameters for enabling tab completion of an argument's value. Only one
- can be used at a time.
-
- choices
- Pass a list of values to the choices parameter.
- Example:
- parser.add_argument('-o', '--options', choices=['An Option', 'SomeOtherOption'])
- parser.add_argument('-o', '--options', choices=my_list)
-
- choices_function
- Pass a function that returns choices. This is good in cases where the choice list is dynamically
- generated when the user hits tab.
-
- Example:
- def my_choices_function():
- ...
- return my_generated_list
-
- parser.add_argument('-o', '--options', choices_function=my_choices_function)
-
- choices_method
- This is exactly like choices_function, but the function needs to be an instance method of a cmd2-based class.
- When ArgparseCompleter calls the method, it will pass the app instance as the self argument. This is good in
- cases where the list of choices being generated relies on state data of the cmd2-based app
-
- Example:
- def my_choices_method(self):
- ...
- return my_generated_list
-
- completer_function
- Pass a tab completion function that does custom completion. Since custom tab completion operations commonly
- need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer
- function. completer_method should be used in those cases.
-
- Example:
- def my_completer_function(text, line, begidx, endidx):
- ...
- return completions
- parser.add_argument('-o', '--options', completer_function=my_completer_function)
-
- completer_method
- This is exactly like completer_function, but the function needs to be an instance method of a cmd2-based class.
- When ArgparseCompleter calls the method, it will pass the app instance as the self argument. cmd2 provides
- a few completer methods for convenience (e.g., path_complete, delimiter_complete)
-
- Example:
- This adds file-path completion to an argument
- parser.add_argument('-o', '--options', completer_method=cmd2.Cmd.path_complete)
-
-
- You can use functools.partial() to prepopulate values of the underlying choices and completer functions/methods.
-
- Example:
- This says to call path_complete with a preset value for its path_filter argument.
- completer_method = functools.partial(path_complete,
- path_filter=lambda path: os.path.isdir(path))
- parser.add_argument('-o', '--options', choices_method=completer_method)
-
- Of the 5 tab completion parameters, choices is the only one where argparse validates user input against items
- in the choices list. This is because the other 4 parameters are meant to tab complete data sets that are viewed
- as dynamic. Therefore it is up to the developer to validate if the user has typed an acceptable value for these
- arguments.
-
- The following functions exist in cases where you may want to manually add a choice-providing function/method to
- an existing argparse action. For instance, in __init__() of a custom action class.
-
- set_choices_function(action, func)
- set_choices_method(action, method)
- set_completer_function(action, func)
- set_completer_method(action, method)
-
- There are times when what's being tab completed is determined by a previous argument on the command line.
- In theses cases, Autocompleter can pass a dictionary that maps the command line tokens up through the one
- being completed to their argparse argument name. To receive this dictionary, your choices/completer function
- should have an argument called arg_tokens.
-
- Example:
- def my_choices_method(self, arg_tokens)
- def my_completer_method(self, text, line, begidx, endidx, arg_tokens)
-
- All values of the arg_tokens dictionary are lists, even if a particular argument expects only 1 token. Since
- ArgparseCompleter is for tab completion, it does not convert the tokens to their actual argument types or validate
- their values. All tokens are stored in the dictionary as the raw strings provided on the command line. It is up to
- the developer to determine if the user entered the correct argument type (e.g. int) and validate their values.
-
-CompletionItem Class:
- This class was added to help in cases where uninformative data is being tab completed. For instance,
- tab completing ID numbers isn't very helpful to a user without context. Returning a list of CompletionItems
- instead of a regular string for completion results will signal the ArgparseCompleter to output the completion
- results in a table of completion tokens with descriptions instead of just a table of tokens.
+**Added capabilities**
+
+Extends argparse nargs functionality by allowing tuples which specify a range
+(min, max). To specify a max value with no upper bound, use a 1-item tuple
+(min,)
+
+Example::
+
+ # -f argument expects at least 3 values
+ parser.add_argument('-f', nargs=(3,))
+
+ # -f argument expects 3 to 5 values
+ parser.add_argument('-f', nargs=(3, 5))
+
+
+**Tab Completion**
+
+cmd2 uses its ArgparseCompleter class to enable argparse-based tab completion
+on all commands that use the @with_argparse wrappers. Out of the box you get
+tab completion of commands, subcommands, and flag names, as well as instructive
+hints about the current argument that print when tab is pressed. In addition,
+you can add tab completion for each argument's values using parameters passed
+to add_argument().
+
+Below are the 5 add_argument() parameters for enabling tab completion of an
+argument's value. Only one can be used at a time.
+
+``choices`` - pass a list of values to the choices parameter.
+
+ Example::
+
+ parser.add_argument('-o', '--options', choices=['An Option', 'SomeOtherOption'])
+ parser.add_argument('-o', '--options', choices=my_list)
+
+``choices_function`` - pass a function that returns choices. This is good in
+cases where the choice list is dynamically generated when the user hits tab.
+
+ Example::
+
+ def my_choices_function():
+ ...
+ return my_generated_list
+
+ parser.add_argument('-o', '--options', choices_function=my_choices_function)
+
+``choices_method`` - this is exactly like choices_function, but the function
+needs to be an instance method of a cmd2-based class. When ArgparseCompleter
+calls the method, it will pass the app instance as the self argument. This is
+good in cases where the list of choices being generated relies on state data of
+the cmd2-based app
+
+ Example::
+
+ def my_choices_method(self):
+ ...
+ return my_generated_list
+
+``completer_function`` - pass a tab completion function that does custom
+completion. Since custom tab completion operations commonly need to modify
+cmd2's instance variables related to tab completion, it will be rare to need a
+completer function. completer_method should be used in those cases.
+
+ Example::
+
+ def my_completer_function(text, line, begidx, endidx):
+ ...
+ return completions
+ parser.add_argument('-o', '--options', completer_function=my_completer_function)
+
+``completer_method`` - this is exactly like completer_function, but the
+function needs to be an instance method of a cmd2-based class. When
+ArgparseCompleter calls the method, it will pass the app instance as the self
+argument. cmd2 provides a few completer methods for convenience (e.g.,
+path_complete, delimiter_complete)
+
+ Example::
+
+ # this adds file-path completion to an argument
+ parser.add_argument('-o', '--options', completer_method=cmd2.Cmd.path_complete)
+
+
+ You can use functools.partial() to prepopulate values of the underlying
+ choices and completer functions/methods.
+
+ Example::
+
+ # this says to call path_complete with a preset value for its path_filter argument.
+ completer_method = functools.partial(path_complete,
+ path_filter=lambda path: os.path.isdir(path))
+ parser.add_argument('-o', '--options', choices_method=completer_method)
+
+Of the 5 tab completion parameters, choices is the only one where argparse
+validates user input against items in the choices list. This is because the
+other 4 parameters are meant to tab complete data sets that are viewed as
+dynamic. Therefore it is up to the developer to validate if the user has typed
+an acceptable value for these arguments.
+
+The following functions exist in cases where you may want to manually add a
+choice-providing function/method to an existing argparse action. For instance,
+in __init__() of a custom action class.
+
+ - set_choices_function(action, func)
+ - set_choices_method(action, method)
+ - set_completer_function(action, func)
+ - set_completer_method(action, method)
+
+There are times when what's being tab completed is determined by a previous
+argument on the command line. In theses cases, Autocompleter can pass a
+dictionary that maps the command line tokens up through the one being completed
+to their argparse argument name. To receive this dictionary, your
+choices/completer function should have an argument called arg_tokens.
+
+ Example::
+
+ def my_choices_method(self, arg_tokens)
+ def my_completer_method(self, text, line, begidx, endidx, arg_tokens)
+
+All values of the arg_tokens dictionary are lists, even if a particular
+argument expects only 1 token. Since ArgparseCompleter is for tab completion,
+it does not convert the tokens to their actual argument types or validate their
+values. All tokens are stored in the dictionary as the raw strings provided on
+the command line. It is up to the developer to determine if the user entered
+the correct argument type (e.g. int) and validate their values.
+
+CompletionItem Class - This class was added to help in cases where
+uninformative data is being tab completed. For instance, tab completing ID
+numbers isn't very helpful to a user without context. Returning a list of
+CompletionItems instead of a regular string for completion results will signal
+the ArgparseCompleter to output the completion results in a table of completion
+tokens with descriptions instead of just a table of tokens::
Instead of this:
1 2 3
@@ -133,42 +156,50 @@ CompletionItem Class:
3 Yet another item
- The left-most column is the actual value being tab completed and its header is that value's name.
- The right column header is defined using the descriptive_header parameter of add_argument(). The right
- column values come from the CompletionItem.description value.
+The left-most column is the actual value being tab completed and its header is
+that value's name. The right column header is defined using the
+descriptive_header parameter of add_argument(). The right column values come
+from the CompletionItem.description value.
+
+Example::
- Example:
- token = 1
- token_description = "My Item"
- completion_item = CompletionItem(token, token_description)
+ token = 1
+ token_description = "My Item"
+ completion_item = CompletionItem(token, token_description)
- Since descriptive_header and CompletionItem.description are just strings, you can format them in
- such a way to have multiple columns.
+Since descriptive_header and CompletionItem.description are just strings, you
+can format them in such a way to have multiple columns::
ITEM_ID Item Name Checked Out Due Date
1 My item True 02/02/2022
2 Another item False
3 Yet another item False
- To use CompletionItems, just return them from your choices or completer functions.
+To use CompletionItems, just return them from your choices or completer
+functions.
- To avoid printing a ton of information to the screen at once when a user presses tab, there is
- a maximum threshold for the number of CompletionItems that will be shown. Its value is defined
- in cmd2.Cmd.max_completion_items. It defaults to 50, but can be changed. If the number of completion
- suggestions exceeds this number, they will be displayed in the typical columnized format and will
- not include the description value of the CompletionItems.
+To avoid printing a ton of information to the screen at once when a user
+presses tab, there is a maximum threshold for the number of CompletionItems
+that will be shown. Its value is defined in cmd2.Cmd.max_completion_items. It
+defaults to 50, but can be changed. If the number of completion suggestions
+exceeds this number, they will be displayed in the typical columnized format
+and will not include the description value of the CompletionItems.
-############################################################################################################
-# Patched argparse functions:
-###########################################################################################################
-argparse._ActionsContainer.add_argument - adds arguments related to tab completion and enables nargs range parsing
- See _add_argument_wrapper for more details on these argument
+
+**Patched argparse functions**
+
+argparse._ActionsContainer.add_argument - adds arguments related to tab
+ completion and enables nargs range
+ parsing See _add_argument_wrapper for
+ more details on these argument
argparse.ArgumentParser._get_nargs_pattern - adds support to for nargs ranges
- See _get_nargs_pattern_wrapper for more details
+ See _get_nargs_pattern_wrapper for
+ more details
-argparse.ArgumentParser._match_argument - adds support to for nargs ranges
- See _match_argument_wrapper for more details
+argparse.ArgumentParser._match_argument - adds support to for nargs ranges See
+ _match_argument_wrapper for more
+ details
"""
import argparse
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index cc80c906..b2d745ac 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -135,28 +135,41 @@ class Cmd(cmd.Cmd):
allow_cli_args: bool = True, transcript_files: Optional[List[str]] = None,
allow_redirection: bool = True, multiline_commands: Optional[List[str]] = None,
terminators: Optional[List[str]] = None, shortcuts: Optional[Dict[str, str]] = None) -> None:
- """An easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
+ """An easy but powerful framework for writing line-oriented command
+ interpreters. Extends Python's cmd package.
:param completekey: readline name of a completion key, default to Tab
:param stdin: alternate input file object, if not specified, sys.stdin is used
:param stdout: alternate output file object, if not specified, sys.stdout is used
:param persistent_history_file: file path to load a persistent cmd2 command history from
- :param persistent_history_length: max number of history items to write to the persistent history file
+ :param persistent_history_length: max number of history items to write
+ to the persistent history file
:param startup_script: file path to a script to execute at startup
:param use_ipython: should the "ipy" command be included for an embedded IPython shell
- :param allow_cli_args: if True, then cmd2 will process command line arguments as either
- commands to be run or, if -t is specified, transcript files to run.
- This should be set to False if your application parses its own arguments.
- :param transcript_files: allow running transcript tests when allow_cli_args is False
- :param allow_redirection: should output redirection and pipes be allowed. this is only a security setting
- and does not alter parsing behavior.
+ :param allow_cli_args: if ``True``, then :meth:`cmd2.Cmd.__init__` will process command
+ line arguments as either commands to be run or, if ``-t`` or
+ ``--test`` are given, transcript files to run. This should be
+ set to ``False`` if your application parses its own command line
+ arguments.
+ :param transcript_files: pass a list of transcript files to be run on initialization.
+ This allows running transcript tests when ``allow_cli_args``
+ is ``False``. If ``allow_cli_args`` is ``True`` this parameter
+ is ignored.
+ :param allow_redirection: If ``False``, prevent output redirection and piping to shell
+ commands. This parameter prevents redirection and piping, but
+ does not alter parsing behavior. A user can still type
+ redirection and piping tokens, and they will be parsed as such
+ but they won't do anything.
:param multiline_commands: list of commands allowed to accept multi-line input
- :param terminators: list of characters that terminate a command. These are mainly intended for terminating
- multiline commands, but will also terminate single-line commands. If not supplied, then
- defaults to semicolon. If your app only contains single-line commands and you want
- terminators to be treated as literals by the parser, then set this to an empty list.
- :param shortcuts: dictionary containing shortcuts for commands. If not supplied, then defaults to
- constants.DEFAULT_SHORTCUTS.
+ :param terminators: list of characters that terminate a command. These are mainly
+ intended for terminating multiline commands, but will also
+ terminate single-line commands. If not supplied, the default
+ is a semicolon. If your app only contains single-line commands
+ and you want terminators to be treated as literals by the parser,
+ then set this to an empty list.
+ :param shortcuts: dictionary containing shortcuts for commands. If not supplied,
+ then defaults to constants.DEFAULT_SHORTCUTS. If you do not want
+ any shortcuts, pass an empty dictionary.
"""
# If use_ipython is False, make sure the ipy command isn't available in this instance
if not use_ipython:
@@ -192,7 +205,7 @@ class Cmd(cmd.Cmd):
# A dictionary mapping settable names to their Settable instance
self.settables = dict()
- self.build_settables()
+ self._build_settables()
# Use as prompt for multiline commands on the 2nd+ line of input
self.continuation_prompt = '> '
@@ -374,24 +387,26 @@ class Cmd(cmd.Cmd):
def add_settable(self, settable: Settable) -> None:
"""
- Convenience method to add a settable parameter to self.settables
+ Convenience method to add a settable parameter to ``self.settables``
+
:param settable: Settable object being added
"""
self.settables[settable.name] = settable
def remove_settable(self, name: str) -> None:
"""
- Convenience method for removing a settable parameter from self.settables
+ Convenience method for removing a settable parameter from ``self.settables``
+
:param name: name of the settable being removed
- :raises: KeyError if the no Settable matches this name
+ :raises: KeyError if the Settable matches this name
"""
try:
del self.settables[name]
except KeyError:
raise KeyError(name + " is not a settable parameter")
- def build_settables(self):
- """Populates self.add_settable with parameters that can be edited via the set command"""
+ def _build_settables(self):
+ """Construct the default settings"""
self.add_settable(Settable('allow_style', str,
'Allow ANSI text style sequences in output (valid values: '
'{}, {}, {})'.format(ansi.STYLE_TERMINAL,
@@ -1508,13 +1523,54 @@ class Cmd(cmd.Cmd):
raise KeyboardInterrupt("Got a keyboard interrupt")
def precmd(self, statement: Statement) -> Statement:
- """Hook method executed just before the command is processed by ``onecmd()`` and after adding it to the history.
+ """Hook method executed just before the command is executed by
+ :meth:`~cmd2.Cmd.onecmd` and after adding it to history.
:param statement: subclass of str which also contains the parsed input
:return: a potentially modified version of the input Statement object
+
+ See :meth:`~cmd2.Cmd.register_postparsing_hook` and
+ :meth:`~cmd2.Cmd.register_precmd_hook` for more robust ways
+ to run hooks before the command is executed. See
+ :ref:`features/hooks:Postparsing Hooks` and
+ :ref:`features/hooks:Precommand Hooks` for more information.
"""
return statement
+ def postcmd(self, stop: bool, statement: Statement) -> bool:
+ """Hook method executed just after a command is executed by
+ :meth:`~cmd2.Cmd.onecmd`.
+
+ :param stop: return `True` to request the command loop terminate
+ :param statement: subclass of str which also contains the parsed input
+
+ See :meth:`~cmd2.Cmd.register_postcmd_hook` and :meth:`~cmd2.Cmd.register_cmdfinalization_hook` for more robust ways
+ to run hooks after the command is executed. See
+ :ref:`features/hooks:Postcommand Hooks` and
+ :ref:`features/hooks:Command Finalization Hooks` for more information.
+ """
+ return stop
+
+ def preloop(self):
+ """Hook method executed once when the :meth:`~.cmd2.Cmd.cmdloop()`
+ method is called.
+
+ See :meth:`~cmd2.Cmd.register_preloop_hook` for a more robust way
+ to run hooks before the command loop begins. See
+ :ref:`features/hooks:Application Lifecycle Hooks` for more information.
+ """
+ pass
+
+ def postloop(self):
+ """Hook method executed once when the :meth:`~.cmd2.Cmd.cmdloop()`
+ method is about to return.
+
+ See :meth:`~cmd2.Cmd.register_postloop_hook` for a more robust way
+ to run hooks after the command loop completes. See
+ :ref:`features/hooks:Application Lifecycle Hooks` for more information.
+ """
+ pass
+
def parseline(self, line: str) -> Tuple[str, str, str]:
"""Parse the line into a command name and a string containing the arguments.
diff --git a/cmd2/constants.py b/cmd2/constants.py
index bc72817f..d7e52cc9 100644
--- a/cmd2/constants.py
+++ b/cmd2/constants.py
@@ -1,6 +1,10 @@
#
# coding=utf-8
-"""Constants and definitions"""
+"""This module contains constants used throughout ``cmd2``."""
+
+# Unless documented in https://cmd2.readthedocs.io/en/latest/api/index.html
+# nothing here should be considered part of the public API of this module
+
# Used for command parsing, output redirection, tab completion and word
# breaks. Do not change.
@@ -32,9 +36,9 @@ HELP_FUNC_PREFIX = 'help_'
# All command completer functions start with this
COMPLETER_FUNC_PREFIX = 'complete_'
-############################################################################################################
+##############################################################################
# The following are optional attributes added to do_* command functions
-############################################################################################################
+##############################################################################
# The custom help category a command belongs to
CMD_ATTR_HELP_CATEGORY = 'help_category'
diff --git a/cmd2/decorators.py b/cmd2/decorators.py
index ee5db140..e9aff0eb 100644
--- a/cmd2/decorators.py
+++ b/cmd2/decorators.py
@@ -8,7 +8,21 @@ from .parsing import Statement
def with_category(category: str) -> Callable:
- """A decorator to apply a category to a command function."""
+ """A decorator to apply a category to a ``do_*`` command method.
+
+ :param category: the name of the category in which this command should
+ be grouped when displaying the list of commands.
+
+ :Example:
+
+ >>> class MyApp(cmd2.Cmd):
+ >>> @cmd2.with_category('Text Functions')
+ >>> def do_echo(self, args)
+ >>> self.poutput(args)
+
+ For an alternative approach to categorizing commands using a function, see
+ :func:`~cmd2.utils.categorize`
+ """
def cat_decorator(func):
from .utils import categorize
categorize(func, category)
@@ -17,12 +31,22 @@ def with_category(category: str) -> Callable:
def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) -> Callable[[List], Optional[bool]]:
- """A decorator to alter the arguments passed to a do_* cmd2 method. Default passes a string of whatever the user
- typed. With this decorator, the decorated method will receive a list of arguments parsed from user input.
+ """
+ A decorator to alter the arguments passed to a ``do_*`` method. Default
+ passes a string of whatever the user typed. With this decorator, the
+ decorated method will receive a list of arguments parsed from user input.
- :param args: Single-element positional argument list containing do_* method this decorator is wrapping
- :param preserve_quotes: if True, then argument quotes will not be stripped
+ :param args: Single-element positional argument list containing ``do_*`` method
+ this decorator is wrapping
+ :param preserve_quotes: if ``True``, then argument quotes will not be stripped
:return: function that gets passed a list of argument strings
+
+ :Example:
+
+ >>> class MyApp(cmd2.Cmd):
+ >>> @cmd2.with_argument_list
+ >>> def do_echo(self, arglist):
+ >>> self.poutput(' '.join(arglist)
"""
import functools
@@ -75,18 +99,19 @@ def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *,
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
preserve_quotes: bool = False) -> \
Callable[[argparse.Namespace, List], Optional[bool]]:
- """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given
- instance of argparse.ArgumentParser, but also returning unknown args as a list.
+ """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing
+ arguments with the given instance of argparse.ArgumentParser, but also returning
+ unknown args as a list.
:param parser: unique instance of ArgumentParser
- :param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an
- argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
- state data that affects parsing.
- :param preserve_quotes: if True, then arguments passed to argparse maintain their quotes
- :return: function that gets passed argparse-parsed args in a Namespace and a list of unknown argument strings
- A member called __statement__ is added to the Namespace to provide command functions access to the
- Statement object. This can be useful if the command function needs to know the command line.
-
+ :param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument
+ and returns an argparse.Namespace. This is useful if the Namespace
+ needs to be prepopulated with state data that affects parsing.
+ :param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
+ :return: function that gets passed argparse-parsed args in a ``Namespace`` and a list
+ of unknown argument strings. A member called ``__statement__`` is added to the
+ ``Namespace`` to provide command functions access to the :class:`cmd2.Statement`
+ object. This can be useful if the command function needs to know the command line.
"""
import functools
diff --git a/cmd2/history.py b/cmd2/history.py
index 3b18fbeb..7b52aa16 100644
--- a/cmd2/history.py
+++ b/cmd2/history.py
@@ -15,7 +15,7 @@ from .parsing import Statement
@attr.s(frozen=True)
class HistoryItem():
- """Class used to represent one command in the History list"""
+ """Class used to represent one command in the history list"""
_listformat = ' {:>4} {}'
_ex_listformat = ' {:>4}x {}'
@@ -28,16 +28,23 @@ class HistoryItem():
@property
def raw(self) -> str:
- """Return the raw input from the user for this item"""
+ """The raw input from the user for this item.
+
+ Proxy property for ``self.statement.raw``
+ """
return self.statement.raw
@property
def expanded(self) -> str:
- """Return the command as run which includes shortcuts and aliases resolved plus any changes made in hooks"""
+ """Return the command as run which includes shortcuts and aliases resolved
+ plus any changes made in hooks
+
+ Proxy property for ``self.statement.expanded_command_line``
+ """
return self.statement.expanded_command_line
def pr(self, script=False, expanded=False, verbose=False) -> str:
- """Represent a HistoryItem in a pretty fashion suitable for printing.
+ """Represent this item in a pretty fashion suitable for printing.
If you pass verbose=True, script and expanded will be ignored
@@ -72,15 +79,17 @@ class HistoryItem():
class History(list):
- """A list of HistoryItems that knows how to respond to user requests.
+ """A list of :class:`~cmd2.history.HistoryItem` objects with additional methods
+ for searching and managing the list.
+
+ :class:`~cmd2.Cmd` instantiates this class into the :data:`~cmd2.Cmd.history`
+ attribute, and adds commands to it as a user enters them.
- Here are some key methods:
+ See :ref:`features/history:History` for information about the built-in command
+ which allows users to view, search, run, and save previously entered commands.
- select() - parse user input and return a list of relevant history items
- str_search() - return a list of history items which contain the given string
- regex_search() - return a list of history items which match a given regex
- get() - return a single element of the list, using 1 based indexing
- span() - given a 1-based slice, return the appropriate list of history items
+ Developers interested in accessing previously entered commands can use this
+ class to gain access to the historical record.
"""
def __init__(self, seq=()) -> None:
super().__init__(seq)
@@ -99,9 +108,10 @@ class History(list):
return result
def append(self, new: Statement) -> None:
- """Append a HistoryItem to end of the History list.
+ """Append a new statement to the end of the History list.
- :param new: command line to convert to HistoryItem and add to the end of the History list
+ :param new: Statement object which will be composed into a HistoryItem
+ and added to the end of the list
"""
history_item = HistoryItem(new, len(self) + 1)
super().append(history_item)
@@ -115,7 +125,7 @@ class History(list):
"""Get item from the History list using 1-based indexing.
:param index: optional item to get (index as either integer or string)
- :return: a single HistoryItem
+ :return: a single :class:`~cmd2.history.HistoryItem`
"""
index = int(index)
if index == 0:
@@ -171,8 +181,8 @@ class History(list):
Different from native python indexing and slicing of arrays, this method
uses 1-based array numbering. Users who are not programmers can't grok
- 0 based numbering. Programmers can usually grok either. Which reminds me,
- there are only two hard problems in programming:
+ zero based numbering. Programmers can sometimes grok zero based numbering.
+ Which reminds me, there are only two hard problems in programming:
- naming
- cache invalidation
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index cef0b088..078b1860 100755
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -76,68 +76,32 @@ class Macro:
class Statement(str):
"""String subclass with additional attributes to store the results of parsing.
- The cmd module in the standard library passes commands around as a
- string. To retain backwards compatibility, cmd2 does the same. However, we
- need a place to capture the additional output of the command parsing, so we add
- our own attributes to this subclass.
+ The ``cmd`` module in the standard library passes commands around as a
+ string. To retain backwards compatibility, ``cmd2`` does the same. However,
+ we need a place to capture the additional output of the command parsing, so
+ we add our own attributes to this subclass.
Instances of this class should not be created by anything other than the
- `StatementParser.parse()` method, nor should any of the attributes be modified
- once the object is created.
-
- The string portion of the class contains the arguments, but not the command, nor
- the output redirection clauses.
-
- Here's some suggestions and best practices for how to use the attributes of this
- object:
-
- command - the name of the command, shortcuts and aliases have already been
- expanded
-
- args - the arguments to the command, excluding output redirection and command
- terminators. If the user used quotes in their input, they remain here,
- and you will have to handle them on your own.
-
- arg_list - the arguments to the command, excluding output redirection and
- command terminators. Each argument is represented as an element
- in the list. Quoted arguments remain quoted. If you want to
- remove the quotes, use `cmd2.utils.strip_quotes()` or use
- `argv[1:]`
-
- command_and_args - join the args and the command together with a space. Output
- redirection is excluded.
-
- argv - this is a list of arguments in the style of `sys.argv`. The first element
- of the list is the command. Subsequent elements of the list contain any
- additional arguments, with quotes removed, just like bash would. This
- is very useful if you are going to use `argparse.parse_args()`:
- ```
- def do_mycommand(stmt):
- mycommand_argparser.parse_args(stmt.argv)
- ...
- ```
-
- raw - if you want full access to exactly what the user typed at the input prompt
- you can get it, but you'll have to parse it on your own, including:
- - shortcuts and aliases
- - quoted commands and arguments
- - output redirection
- - multi-line command terminator handling
- if you use multiline commands, all the input will be passed to you in
- this string, but there will be embedded newlines where
- the user hit return to continue the command on the next line.
+ :meth:`cmd2.parsing.StatementParser.parse` method, nor should any of the
+ attributes be modified once the object is created.
+
+ The string portion of the class contains the arguments, but not the
+ command, nor the output redirection clauses.
Tips:
- 1. `argparse` is your friend for anything complex. `cmd2` has two decorators
- (`with_argparser`, and `with_argparser_and_unknown_args`) which you can use
- to make your command method receive a namespace of parsed arguments, whether
- positional or denoted with switches.
+ 1. `argparse <https://docs.python.org/3/library/argparse.html>`_ is your
+ friend for anything complex. ``cmd2`` has two decorators
+ (:func:`~cmd2.decorators.with_argparser`, and
+ :func:`~cmd2.decorators.with_argparser_and_unknown_args`) which you can
+ use to make your command method receive a namespace of parsed arguments,
+ whether positional or denoted with switches.
- 2. For commands with simple positional arguments, use `args` or `arg_list`
+ 2. For commands with simple positional arguments, use
+ :attr:`~cmd2.Statement.args` or :attr:`~cmd2.Statement.arg_list`
- 3. If you don't want to have to worry about quoted arguments, use
- argv[1:], which strips them all off for you.
+ 3. If you don't want to have to worry about quoted arguments, see
+ :attr:`argv` for a trick which strips quotes off for you.
"""
# the arguments, but not the command, nor the output redirection clauses.
args = attr.ib(default='', validator=attr.validators.instance_of(str))
@@ -219,15 +183,20 @@ class Statement(str):
@property
def expanded_command_line(self) -> str:
- """Combines command_and_args and post_command"""
+ """Concatenate :meth:`~cmd2.Statement.command_and_args`
+ and :meth:`~cmd2.Statement.post_command`"""
return self.command_and_args + self.post_command
@property
def argv(self) -> List[str]:
- """a list of arguments a la sys.argv.
+ """a list of arguments a-la ``sys.argv``.
+
+ The first element of the list is the command after shortcut and macro
+ expansion. Subsequent elements of the list contain any additional
+ arguments, with quotes removed, just like bash would. This is very
+ useful if you are going to use ``argparse.parse_args()``.
- Quotes, if any, are removed from the elements of the list, and aliases
- and shortcuts are expanded
+ If you want to strip quotes from the input, you can use ``argv[1:]``.
"""
if self.command:
rtn = [utils.strip_quotes(self.command)]
@@ -240,11 +209,7 @@ class Statement(str):
class StatementParser:
- """Parse raw text into command components.
-
- Shortcuts is a list of tuples with each tuple containing the shortcut and
- the expansion.
- """
+ """Parse user input as a string into discrete command components."""
def __init__(self,
terminators: Optional[Iterable[str]] = None,
multiline_commands: Optional[Iterable[str]] = None,
@@ -253,9 +218,7 @@ class StatementParser:
"""Initialize an instance of StatementParser.
The following will get converted to an immutable tuple before storing internally:
- * terminators
- * multiline commands
- * shortcuts
+ terminators, multiline commands, and shortcuts.
:param terminators: iterable containing strings which should terminate commands
:param multiline_commands: iterable containing the names of commands that accept multiline input
@@ -321,13 +284,16 @@ class StatementParser:
or termination characters. They also cannot start with a
shortcut.
- If word is not a valid command, return False and error text
- This string is suitable for inclusion in an error message of your
- choice:
+ :param word: the word to check as a command
+ :return: a tuple of a boolean and an error string
+
+ If word is not a valid command, return ``False`` and an error string
+ suitable for inclusion in an error message of your choice::
- valid, errmsg = statement_parser.is_valid_command('>')
- if not valid:
- errmsg = "Alias {}".format(errmsg)
+ checkit = '>'
+ valid, errmsg = statement_parser.is_valid_command(checkit)
+ if not valid:
+ errmsg = "alias: {}".format(errmsg)
"""
valid = False
@@ -359,11 +325,12 @@ class StatementParser:
def tokenize(self, line: str) -> List[str]:
"""
- Lex a string into a list of tokens. Shortcuts and aliases are expanded and comments are removed
+ Lex a string into a list of tokens. Shortcuts and aliases are expanded and
+ comments are removed.
:param line: the command line being lexed
:return: A list of tokens
- :raises ValueError if there are unclosed quotation marks.
+ :raises ValueError: if there are unclosed quotation marks
"""
# expand shortcuts and aliases
@@ -382,13 +349,13 @@ class StatementParser:
def parse(self, line: str) -> Statement:
"""
- Tokenize the input and parse it into a Statement object, stripping
- comments, expanding aliases and shortcuts, and extracting output
+ Tokenize the input and parse it into a :class:`~cmd2.Statement` object,
+ stripping comments, expanding aliases and shortcuts, and extracting output
redirection directives.
:param line: the command line being parsed
- :return: the created Statement
- :raises ValueError if there are unclosed quotation marks
+ :return: a new :class:`~cmd2.Statement` object
+ :raises ValueError: if there are unclosed quotation marks
"""
# handle the special case/hardcoded terminator of a blank line
@@ -526,7 +493,7 @@ class StatementParser:
return statement
def parse_command_only(self, rawinput: str) -> Statement:
- """Partially parse input into a Statement object.
+ """Partially parse input into a :class:`~cmd2.Statement` object.
The command is identified, and shortcuts and aliases are expanded.
Multiline commands are identified, but terminators and output
@@ -535,22 +502,21 @@ class StatementParser:
This method is used by tab completion code and therefore must not
generate an exception if there are unclosed quotes.
- The `Statement` object returned by this method can at most contain values
- in the following attributes:
- - args
- - raw
- - command
- - multiline_command
+ The :class:`~cmd2.Statement` object returned by this method can at most
+ contain values in the following attributes:
+ :attr:`~cmd2.Statement.args`, :attr:`~cmd2.Statement.raw`,
+ :attr:`~cmd2.Statement.command`,
+ :attr:`~cmd2.Statement.multiline_command`
- `Statement.args` includes all output redirection clauses and command
- terminators.
+ :attr:`~cmd2.Statement.args` will include all output redirection
+ clauses and command terminators.
- Different from parse(), this method does not remove redundant whitespace
- within args. However, it does ensure args has no leading or trailing
- whitespace.
+ Different from :meth:`~cmd2.parsing.StatementParser.parse` this method
+ does not remove redundant whitespace within args. However, it does
+ ensure args has no leading or trailing whitespace.
:param rawinput: the command line as entered by the user
- :return: the created Statement
+ :return: a new :class:`~cmd2.Statement` object
"""
line = rawinput
@@ -590,22 +556,26 @@ class StatementParser:
def get_command_arg_list(self, command_name: str, to_parse: Union[Statement, str],
preserve_quotes: bool) -> Tuple[Statement, List[str]]:
"""
- Called by the argument_list and argparse wrappers to retrieve just the arguments being
- passed to their do_* methods as a list.
+ Convenience method used by the argument parsing decorators.
+
+ Retrieves just the arguments being passed to their ``do_*`` methods as a list.
:param command_name: name of the command being run
- :param to_parse: what is being passed to the do_* method. It can be one of two types:
- 1. An already parsed Statement
- 2. An argument string in cases where a do_* method is explicitly called
- e.g.: Calling do_help('alias create') would cause to_parse to be 'alias create'
-
- In this case, the string will be converted to a Statement and returned along
- with the argument list.
-
- :param preserve_quotes: if True, then quotes will not be stripped from the arguments
- :return: A tuple containing:
- The Statement used to retrieve the arguments
- The argument list
+ :param to_parse: what is being passed to the ``do_*`` method. It can be one of two types:
+
+ 1. An already parsed :class:`~cmd2.Statement`
+ 2. An argument string in cases where a ``do_*`` method is
+ explicitly called. Calling ``do_help('alias create')`` would
+ cause ``to_parse`` to be 'alias create'.
+
+ In this case, the string will be converted to a
+ :class:`~cmd2.Statement` and returned along with
+ the argument list.
+
+ :param preserve_quotes: if ``True``, then quotes will not be stripped from
+ the arguments
+ :return: A tuple containing the :class:`~cmd2.Statement` and a list of
+ strings representing the arguments
"""
# Check if to_parse needs to be converted to a Statement
if not isinstance(to_parse, Statement):
@@ -669,14 +639,14 @@ class StatementParser:
return command, args
def split_on_punctuation(self, tokens: List[str]) -> List[str]:
- """Further splits tokens from a command line using punctuation characters
+ """Further splits tokens from a command line using punctuation characters.
Punctuation characters are treated as word breaks when they are in
unquoted strings. Each run of punctuation characters is treated as a
single token.
:param tokens: the tokens as parsed by shlex
- :return: the punctuated tokens
+ :return: a new list of tokens, further split using punctuation
"""
punctuation = []
punctuation.extend(self.terminators)
diff --git a/cmd2/plugin.py b/cmd2/plugin.py
index dc9ec297..83093ee1 100644
--- a/cmd2/plugin.py
+++ b/cmd2/plugin.py
@@ -6,22 +6,26 @@ import attr
@attr.s
class PostparsingData:
+ """Data class containing information passed to postparsing hook methods"""
stop = attr.ib()
statement = attr.ib()
@attr.s
class PrecommandData:
+ """Data class containing information passed to precommand hook methods"""
statement = attr.ib()
@attr.s
class PostcommandData:
+ """Data class containing information passed to postcommand hook methods"""
stop = attr.ib()
statement = attr.ib()
@attr.s
class CommandFinalizationData:
+ """Data class containing information passed to command finalization hook methods"""
stop = attr.ib()
statement = attr.ib()
diff --git a/cmd2/py_bridge.py b/cmd2/py_bridge.py
index b7346d22..6624d7ad 100644
--- a/cmd2/py_bridge.py
+++ b/cmd2/py_bridge.py
@@ -1,7 +1,7 @@
# coding=utf-8
"""
-Bridges calls made inside of a Python environment to the Cmd2 host app while maintaining a reasonable
-degree of isolation between the two
+Bridges calls made inside of a Python environment to the Cmd2 host app
+while maintaining a reasonable degree of isolation between the two.
"""
import sys
@@ -14,32 +14,38 @@ from .utils import namedtuple_with_defaults, StdSim
class CommandResult(namedtuple_with_defaults('CommandResult', ['stdout', 'stderr', 'stop', 'data'])):
"""Encapsulates the results from a cmd2 app command
- Named tuple attributes
- ----------------------
- stdout: str - output captured from stdout while this command is executing
- stderr: str - output captured from stderr while this command is executing. None if no error captured.
- stop: bool - return value of onecmd_plus_hooks after it runs the given command line.
- data - possible data populated by the command.
+ :stdout: str - output captured from stdout while this command is executing
+ :stderr: str - output captured from stderr while this command is executing
+ None if no error captured.
+ :stop: bool - return value of onecmd_plus_hooks after it runs the given
+ command line.
+ :data: possible data populated by the command.
- Any combination of these fields can be used when developing a scripting API for a given command.
- By default stdout, stderr, and stop will be captured for you. If there is additional command specific data,
- then write that to cmd2's last_result member. That becomes the data member of this tuple.
+ Any combination of these fields can be used when developing a scripting API
+ for a given command. By default stdout, stderr, and stop will be captured
+ for you. If there is additional command specific data, then write that to
+ cmd2's last_result member. That becomes the data member of this tuple.
- In some cases, the data member may contain everything needed for a command and storing stdout
- and stderr might just be a duplication of data that wastes memory. In that case, the StdSim can
- be told not to store output with its pause_storage member. While this member is True, any output
- sent to StdSim won't be saved in its buffer.
+ In some cases, the data member may contain everything needed for a command
+ and storing stdout and stderr might just be a duplication of data that
+ wastes memory. In that case, the StdSim can be told not to store output
+ with its pause_storage member. While this member is True, any output sent
+ to StdSim won't be saved in its buffer.
+
+ The code would look like this::
- The code would look like this:
if isinstance(self.stdout, StdSim):
self.stdout.pause_storage = True
if isinstance(sys.stderr, StdSim):
sys.stderr.pause_storage = True
- See StdSim class in utils.py for more information
+ See :class:`~cmd2.utils.StdSim` for more information.
+
+ .. note::
- NOTE: Named tuples are immutable. So the contents are there for access, not for modification.
+ Named tuples are immutable. The contents are there for access,
+ not for modification.
"""
def __bool__(self) -> bool:
"""Returns True if the command succeeded, otherwise False"""
diff --git a/cmd2/utils.py b/cmd2/utils.py
index 237a6d1e..f119999a 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -969,10 +969,22 @@ def get_styles_in_text(text: str) -> Dict[int, str]:
def categorize(func: Union[Callable, Iterable[Callable]], category: str) -> None:
"""Categorize a function.
- The help command output will group this function under the specified category heading
+ The help command output will group the passed function under the
+ specified category heading
:param func: function or list of functions to categorize
:param category: category to put it in
+
+ :Example:
+
+ >>> class MyApp(cmd2.Cmd):
+ >>> def do_echo(self, arglist):
+ >>> self.poutput(' '.join(arglist)
+ >>>
+ >>> utils.categorize(do_echo, "Text Processing")
+
+ For an alternative approach to categorizing commands using a decorator, see
+ :func:`~cmd2.decorators.with_category`
"""
if isinstance(func, Iterable):
for item in func:
diff --git a/docs/api/argparse_completer.rst b/docs/api/argparse_completer.rst
new file mode 100644
index 00000000..0e277582
--- /dev/null
+++ b/docs/api/argparse_completer.rst
@@ -0,0 +1,5 @@
+cmd2.argparse_completer
+=======================
+
+.. automodule:: cmd2.argparse_completer
+ :members:
diff --git a/docs/api/argparse_custom.rst b/docs/api/argparse_custom.rst
new file mode 100644
index 00000000..bdb53e1a
--- /dev/null
+++ b/docs/api/argparse_custom.rst
@@ -0,0 +1,5 @@
+cmd2.argparse_custom
+====================
+
+.. automodule:: cmd2.argparse_custom
+ :members:
diff --git a/docs/api/cmd.rst b/docs/api/cmd.rst
index 213a14ec..3fcd3352 100644
--- a/docs/api/cmd.rst
+++ b/docs/api/cmd.rst
@@ -24,13 +24,42 @@ cmd2.Cmd
.. attribute:: prompt
- The prompt issued to solicit input.
- Default: ``(Cmd)``.
+ The prompt issued to solicit input. The default value is ``(Cmd)``.
+ See :ref:`features/prompt:Prompt` for more information.
+
+ .. attribute:: continuation_prompt
+
+ The prompt issued to solicit input for the 2nd and subsequent lines
+ of a :ref:`multiline command <features/multiline_commands:Multiline Commands>`
+
+ .. attribute:: echo
+
+ If ``True``, output the prompt and user input before executing the command.
+ When redirecting a series of commands to an output file, this allows you to
+ see the command in the output.
.. attribute:: settable
- This dictionary contains the name and description of all settings available to users.
+ This dictionary contains the name and description of all settings
+ available to users.
Users use the :ref:`features/builtin_commands:set` command to view and
modify settings. Settings are stored in instance attributes with the
same name as the setting.
+
+ .. attribute:: history
+
+ A record of previously entered commands.
+
+ This attribute is an instance of :class:`cmd2.history.History`, and
+ each command is an instance of :class:`cmd2.history.HistoryItem`.
+
+ .. attribute:: statement_parser
+
+ An instance of :class:`cmd2.parsing.StatementParser` initialized and
+ configured appropriately for parsing user input.
+
+ .. attribute:: intro
+
+ Set an introduction message which is displayed to the user before
+ the :ref:`features/hooks:Command Processing Loop` begins.
diff --git a/docs/api/constants.rst b/docs/api/constants.rst
new file mode 100644
index 00000000..b48ba462
--- /dev/null
+++ b/docs/api/constants.rst
@@ -0,0 +1,29 @@
+cmd2.constants
+==============
+
+.. automodule:: cmd2.constants
+
+ .. data:: DEFAULT_SHORTCUTS
+
+ If you do not supply shortcuts to :meth:`cmd2.Cmd.__init__`, the shortcuts
+ defined here will be used instead.
+
+
+ .. data:: COMMAND_NAME
+
+ Used by :meth:`cmd2.Cmd.disable_command` and
+ :meth:`cmd2.Cmd.disable_category`. Those methods allow you to selectively
+ disable single commands or an entire category of commands. Should you want
+ to include the name of the command in the error message displayed to the
+ user when they try and run a disabled command, you can include this
+ constant in the message where you would like the name of the command to
+ appear. ``cmd2`` will replace this constant with the name of the command
+ the user tried to run before displaying the error message.
+
+ This constant is imported into the package namespace; the preferred syntax
+ to import and reference it is::
+
+ import cmd2
+ errmsg = "The {} command is currently disabled.".format(cmd2.COMMAND_NAME)
+
+ See ``src/examples/help_categories.py`` for an example.
diff --git a/docs/api/decorators.rst b/docs/api/decorators.rst
index 12ab62fa..c78dcc66 100644
--- a/docs/api/decorators.rst
+++ b/docs/api/decorators.rst
@@ -1,10 +1,5 @@
-Decorators
-==========
+cmd2.decorators
+===============
-.. autofunction:: cmd2.decorators.with_argparser
-
-.. autofunction:: cmd2.decorators.with_argparser_and_unknown_args
-
-.. autofunction:: cmd2.decorators.with_argument_list
-
-.. autofunction:: cmd2.decorators.with_category
+.. automodule:: cmd2.decorators
+ :members:
diff --git a/docs/api/history.rst b/docs/api/history.rst
new file mode 100644
index 00000000..3a3ae2c4
--- /dev/null
+++ b/docs/api/history.rst
@@ -0,0 +1,17 @@
+cmd2.history
+===============
+
+.. autoclass:: cmd2.history.History
+ :members:
+
+
+.. autoclass:: cmd2.history.HistoryItem
+ :members:
+
+ .. attribute:: statement
+
+ The :class:`~cmd2.Statement` object parsed from user input
+
+ .. attribute:: idx
+
+ The 1-based index of this statement in the history list
diff --git a/docs/api/index.rst b/docs/api/index.rst
index 346fc274..d5fc013b 100644
--- a/docs/api/index.rst
+++ b/docs/api/index.rst
@@ -1,11 +1,30 @@
API Reference
=============
+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 documented here.
+When looking at the source code for this library, you can not safely assume
+that because something doesn't start with an underscore, it is a public API.
+
+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>`_.
+
+This documentation is for version |version| of ``cmd2``.
+
.. toctree::
:maxdepth: 1
cmd
+ parsing
decorators
+ history
+ argparse_completer
+ argparse_custom
ansi
- utility_classes
- utility_functions
+ utils
+ plugin
+ py_bridge
+ constants
diff --git a/docs/api/parsing.rst b/docs/api/parsing.rst
new file mode 100644
index 00000000..c79c8f6b
--- /dev/null
+++ b/docs/api/parsing.rst
@@ -0,0 +1,75 @@
+cmd2.parsing
+===============
+
+.. autoclass:: cmd2.Statement
+ :members:
+
+ .. attribute:: command
+
+ The name of the command after shortcuts and macros have been expanded
+
+ .. attribute:: args
+
+ The arguments to the command as a string with spaces between the words,
+ excluding output redirection and command terminators. If the user used
+ quotes in their input, they remain here, and you will have to handle them
+ on your own.
+
+ .. attribute:: arg_list
+
+ The arguments to the command as a list, excluding output
+ redirection and command terminators. Each argument is represented as an
+ element in the list. Quoted arguments remain quoted. If you want to
+ remove the quotes, use :func:`cmd2.utils.strip_quotes` or use
+ ``argv[1:]``
+
+ .. attribute:: raw
+
+ If you want full access to exactly what the user typed at the input
+ prompt you can get it, but you'll have to parse it on your own,
+ including:
+
+ - shortcuts and aliases
+ - quoted commands and arguments
+ - output redirection
+ - multi-line command terminator handling
+
+ If you use multiline commands, all the input will be passed to you in
+ this string, but there will be embedded newlines where the user hit
+ return to continue the command on the next line.
+
+ .. attribute:: multiline_command
+
+ If the command is a multi-line command, the name of the command will be
+ in this attribute. Otherwise, it will be an empty string.
+
+ .. attribute:: terminator
+
+ If the command is a multi-line command, this attribute contains the
+ termination character entered by the user to signal the end of input
+
+ .. attribute:: suffix
+
+ Any characters present between the input terminator and the output
+ redirection tokens.
+
+ .. attribute:: pipe_to
+
+ If the user piped the output to a shell command, this attribute contains
+ the entire shell command as a string. Otherwise it is an empty string.
+
+ .. attribute:: output
+
+ If output was redirected by the user, this contains the redirection
+ token, i.e. ``>>``.
+
+ .. attribute:: output_to
+
+ If output was redirected by the user, this contains the requested destination with
+ quotes preserved.
+
+
+.. autoclass:: cmd2.parsing.StatementParser
+ :members:
+
+ .. automethod:: __init__
diff --git a/docs/api/plugin.rst b/docs/api/plugin.rst
new file mode 100644
index 00000000..4e554b33
--- /dev/null
+++ b/docs/api/plugin.rst
@@ -0,0 +1,45 @@
+cmd2.plugin
+===========
+
+.. autoclass:: cmd2.plugin.PostparsingData
+ :members:
+
+ .. attribute:: stop
+
+ Request the command loop terminate by setting ``True``
+
+ .. attribute:: statement
+
+ The :class:`~cmd2.Statement` object parsed from user input
+
+
+.. autoclass:: cmd2.plugin.PrecommandData
+ :members:
+
+ .. attribute:: statement
+
+ The :class:`~cmd2.Statement` object parsed from user input
+
+
+.. autoclass:: cmd2.plugin.PostcommandData
+ :members:
+
+ .. attribute:: stop
+
+ Request the command loop terminate by setting ``True``
+
+ .. attribute:: statement
+
+ The :class:`~cmd2.Statement` object parsed from user input
+
+
+.. autoclass:: cmd2.plugin.CommandFinalizationData
+ :members:
+
+ .. attribute:: stop
+
+ Request the command loop terminate by setting ``True``
+
+ .. attribute:: statement
+
+ The :class:`~cmd2.Statement` object parsed from user input
diff --git a/docs/api/py_bridge.rst b/docs/api/py_bridge.rst
new file mode 100644
index 00000000..28a70b59
--- /dev/null
+++ b/docs/api/py_bridge.rst
@@ -0,0 +1,5 @@
+cmd2.py_bridge
+==============
+
+.. automodule:: cmd2.py_bridge
+ :members:
diff --git a/docs/api/utility_classes.rst b/docs/api/utility_classes.rst
deleted file mode 100644
index 2ee92ced..00000000
--- a/docs/api/utility_classes.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-Utility Classes
-===============
-
-.. autoclass:: cmd2.utils.Settable
-
- .. automethod:: __init__
-
-.. autoclass:: cmd2.utils.StdSim
-
-.. autoclass:: cmd2.utils.ByteBuf
-
-.. autoclass:: cmd2.utils.ProcReader
-
-.. autoclass:: cmd2.utils.ContextFlag
-
-.. autoclass:: cmd2.utils.RedirectionSavedState
diff --git a/docs/api/utility_functions.rst b/docs/api/utility_functions.rst
deleted file mode 100644
index b348ac1c..00000000
--- a/docs/api/utility_functions.rst
+++ /dev/null
@@ -1,54 +0,0 @@
-Utility Functions
-=================
-
-.. autofunction:: cmd2.utils.is_quoted
-
-.. autofunction:: cmd2.utils.quote_string_if_needed
-
-.. autofunction:: cmd2.utils.strip_quotes
-
-.. autofunction:: cmd2.utils.categorize
-
-.. autofunction:: cmd2.utils.align_text
-
-.. autofunction:: cmd2.utils.align_left
-
-.. autofunction:: cmd2.utils.align_center
-
-.. autofunction:: cmd2.utils.align_right
-
-.. autofunction:: cmd2.utils.truncate_line
-
-.. autofunction:: cmd2.utils.strip_quotes
-
-.. autofunction:: cmd2.utils.namedtuple_with_defaults
-
-.. autofunction:: cmd2.utils.which
-
-.. autofunction:: cmd2.utils.is_text_file
-
-.. autofunction:: cmd2.utils.remove_duplicates
-
-.. autofunction:: cmd2.utils.norm_fold
-
-.. autofunction:: cmd2.utils.try_int_or_force_to_lower_case
-
-.. autofunction:: cmd2.utils.alphabetical_sort
-
-.. autofunction:: cmd2.utils.unquote_specific_tokens
-
-.. autofunction:: cmd2.utils.natural_sort
-
-.. autofunction:: cmd2.utils.natural_keys
-
-.. autofunction:: cmd2.utils.expand_user_in_tokens
-
-.. autofunction:: cmd2.utils.expand_user
-
-.. autofunction:: cmd2.utils.find_editor
-
-.. autofunction:: cmd2.utils.get_exes_in_path
-
-.. autofunction:: cmd2.utils.files_from_glob_patterns
-
-.. autofunction:: cmd2.utils.files_from_glob_pattern
diff --git a/docs/api/utils.rst b/docs/api/utils.rst
new file mode 100644
index 00000000..2ae5987b
--- /dev/null
+++ b/docs/api/utils.rst
@@ -0,0 +1,78 @@
+cmd2.utils
+==========
+
+
+Settings
+--------
+
+.. autoclass:: cmd2.utils.Settable
+ :members:
+
+ .. automethod:: __init__
+
+
+Quote Handling
+--------------
+
+.. autofunction:: cmd2.utils.is_quoted
+
+.. autofunction:: cmd2.utils.quote_string
+
+.. autofunction:: cmd2.utils.quote_string_if_needed
+
+.. autofunction:: cmd2.utils.strip_quotes
+
+
+IO Handling
+-----------
+
+.. autoclass:: cmd2.utils.StdSim
+ :members:
+
+.. autoclass:: cmd2.utils.ByteBuf
+ :members:
+
+.. autoclass:: cmd2.utils.ProcReader
+ :members:
+
+
+Tab Completion
+--------------
+
+.. autoclass:: cmd2.utils.CompletionError
+ :members:
+
+.. autofunction:: cmd2.utils.remove_duplicates
+
+.. autofunction:: cmd2.utils.alphabetical_sort
+
+.. autofunction:: cmd2.utils.natural_sort
+
+.. autofunction:: cmd2.utils.basic_complete
+
+
+Text Alignment
+--------------
+
+.. autoclass:: cmd2.utils.TextAlignment
+ :members:
+
+.. autofunction:: cmd2.utils.align_text
+
+.. autofunction:: cmd2.utils.align_left
+
+.. autofunction:: cmd2.utils.align_right
+
+.. autofunction:: cmd2.utils.align_center
+
+.. autofunction:: cmd2.utils.truncate_line
+
+
+Miscellaneous
+-------------
+
+.. autofunction:: cmd2.utils.str_to_bool
+
+.. autofunction:: cmd2.utils.namedtuple_with_defaults
+
+.. autofunction:: cmd2.utils.categorize
diff --git a/docs/conf.py b/docs/conf.py
index 02eb827f..cd320f52 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -164,3 +164,8 @@ texinfo_documents = [
# -- Options for Extensions -------------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}
+
+# options for autodoc
+autodoc_default_options = {
+ 'member-order': 'bysource'
+}
diff --git a/docs/doc_conventions.rst b/docs/doc_conventions.rst
index 49149d7c..c37a4825 100644
--- a/docs/doc_conventions.rst
+++ b/docs/doc_conventions.rst
@@ -201,6 +201,9 @@ for multiple matching references is high, which causes Sphinx to generate
warnings. The build pipeline that renders the documentation treats warnings as
fatal errors. It's best to just be specific about what you are referencing.
+See `<https://github.com/python-cmd2/cmd2/issues/821>`_ for the discussion of
+how we determined this approach.
+
Referencing cmd2
-----------------
diff --git a/docs/examples/first_app.rst b/docs/examples/first_app.rst
index 310c8d0c..85f84e31 100644
--- a/docs/examples/first_app.rst
+++ b/docs/examples/first_app.rst
@@ -40,15 +40,16 @@ First we need to create a new ``cmd2`` application. Create a new file
sys.exit(c.cmdloop())
We have a new class ``FirstApp`` which is a subclass of
-:ref:`api/cmd:cmd2.Cmd`. When we tell python to run our file like this:
+:class:`cmd2.Cmd`. When we tell python to run our file like this:
.. code-block:: shell
$ python first_app.py
-it creates an instance of our class, and calls the ``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.
+it creates an instance of our class, and calls the :meth:`~cmd2.Cmd.cmdloop`
+method. This method accepts user input and runs commands based on that input.
+Because we subclassed :class:`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.
@@ -70,11 +71,10 @@ initializer to our class::
self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command'))
In that initializer, the first thing to do is to make sure we initialize
-``cmd2``. That's what the ``super().__init__()`` line does. Then we create an
-attribute to hold our setting, and then add a description of our setting to the
-``settable`` dictionary. If our attribute name isn't in ``settable``, then it
-won't be treated as a setting. Now if you run the script, and enter the ``set``
-command to see the settings, like this:
+``cmd2``. That's what the ``super().__init__()`` line does. Next create an
+attribute to hold the setting. Finally, call the :meth:`~cmd2.Cmd.add_settable`
+method with a new instance of a :meth:`~cmd2.utils.Settable` class. Now if you
+run the script, and enter the ``set`` command to see the settings, like this:
.. code-block:: shell
@@ -131,8 +131,8 @@ There is also a new method called ``do_speak()``. In both cmd_ and ``cmd2``,
methods that start with ``do_`` become new commands, so by defining this method
we have created a command called ``speak``.
-Note the ``@cmd2.with_argparser`` decorator on the ``do_speak()`` method. This
-decorator does 3 useful things for us:
+Note the :func:`~cmd2.decorators.with_argparser` decorator on the
+``do_speak()`` method. This decorator does 3 useful things for us:
1. It tells ``cmd2`` to process all input for the ``speak`` command using the
argparser we defined. If the user input doesn't meet the requirements
@@ -159,8 +159,9 @@ benefits:
2. Gracefully handles ``BrokenPipeWarning`` exceptions for redirected output
3. Makes the output show up in a :ref:`transcript
<features/transcripts:Transcripts>`
-4. Honors the setting to strip embedded ansi sequences (typically used for
- background and foreground colors)
+4. Honors the setting to :ref:`strip embedded ansi sequences
+ <features/settings:allow_style>` (typically used for background and
+ foreground colors)
Go run the script again, and try out the ``speak`` command. Try typing ``help
speak``, and you will see a lovely usage message describing the various options
diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst
index 82244d7e..204c2876 100644
--- a/docs/features/argument_processing.rst
+++ b/docs/features/argument_processing.rst
@@ -342,10 +342,8 @@ Argparse Extensions
- ``nargs=(5,)`` - accept 5 or more items
- ``nargs=(8, 12)`` - accept 8 to 12 items
-``cmd2`` also provides the ``Cmd2ArgumentParser`` class which inherits from
-``argparse.ArgumentParser`` and improves error and help output:
-
-.. autoclass:: cmd2.argparse_custom.Cmd2ArgumentParser
- :members:
+``cmd2`` also provides the :class:`cmd2.argparse_custom.Cmd2ArgumentParser`
+class which inherits from ``argparse.ArgumentParser`` and improves error and
+help output.
diff --git a/docs/features/completion.rst b/docs/features/completion.rst
index a8c1cc85..083178dc 100644
--- a/docs/features/completion.rst
+++ b/docs/features/completion.rst
@@ -36,8 +36,8 @@ similar to the following to your class which inherits from ``cmd2.Cmd``::
Tab Completion Using Argparse Decorators
----------------------------------------
-When using one the Argparse-based :ref:`api/decorators:Decorators`, ``cmd2``
-provides automatic tab completion of flag names.
+When using one the Argparse-based :ref:`api/decorators:cmd2.decorators`,
+``cmd2`` provides automatic tab completion of flag names.
Tab completion of argument values can be configured by using one of five
parameters to ``argparse.ArgumentParser.add_argument()``
@@ -69,12 +69,9 @@ 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
-``CompletionItem`` class which can be returned from any of the 4 completion
-functions: ``choices_function``, ``choices_method``, ``completion_function``,
-or ``completion_method``.
-
-.. autoclass:: cmd2.argparse_custom.CompletionItem
- :members:
+:class:`cmd2.argparse_custom.CompletionItem` class which can be returned from
+any of the 4 completion functions: ``choices_function``, ``choices_method``,
+``completion_function``, or ``completion_method``.
See the argparse_completion_ example or the implementation of the built-in
**set** command for demonstration of how this is used.
diff --git a/docs/features/hooks.rst b/docs/features/hooks.rst
index ba9af573..fec8e258 100644
--- a/docs/features/hooks.rst
+++ b/docs/features/hooks.rst
@@ -19,7 +19,11 @@ command processing loop.
Application Lifecycle Hooks
---------------------------
-You can register methods to be called at the beginning of the command loop::
+You can run a script on initialization by passing the script filename in the
+``startup_script`` parameter of :meth:`cmd2.Cmd.__init__`.
+
+You can also register methods to be called at the beginning of the command
+loop::
class App(cmd2.Cmd):
def __init__(self, *args, *kwargs):
@@ -29,8 +33,9 @@ You can register methods to be called at the beginning of the command loop::
def myhookmethod(self):
self.poutput("before the loop begins")
-To retain backwards compatibility with `cmd.Cmd`, after all registered preloop
-hooks have been called, the ``preloop()`` method is called.
+To retain backwards compatibility with ``cmd.Cmd``, after all registered
+preloop hooks have been called, the :meth:`~cmd2.Cmd.preloop` method is
+called.
A similar approach allows you to register functions to be called after the
command loop has finished::
@@ -43,54 +48,68 @@ command loop has finished::
def myhookmethod(self):
self.poutput("before the loop begins")
-To retain backwards compatibility with `cmd.Cmd`, after all registered postloop
-hooks have been called, the ``postloop()`` method is called.
+To retain backwards compatibility with ``cmd.Cmd``, after all registered
+postloop hooks have been called, the :meth:`~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 :ref:`features/plugins:Hooks` for best practices writing hooks.
+
Application Lifecycle Attributes
--------------------------------
-There are numerous attributes of and arguments to ``cmd2.Cmd`` which have a
-significant effect on the application behavior upon entering or during the main
-loop. A partial list of some of the more important ones is presented here:
+There are numerous attributes on :class:`cmd2.Cmd` which affect application
+behavior upon entering or during the command loop:
+
+- :data:`~cmd2.Cmd.intro` - if provided this serves as the intro banner printed
+ once at start of application, after :meth:`~cmd2.Cmd.preloop` is called.
+- :data:`~cmd2.Cmd.prompt` - see :ref:`features/prompt:Prompt` for more
+ information.
+- :data:`~cmd2.Cmd.continuation_prompt` - The prompt issued to solicit input
+ for the 2nd and subsequent lines of a
+ :ref:`multiline command <features/multiline_commands:Multiline Commands>`
+- :data:`~cmd2.Cmd.echo` - if ``True`` write the prompt and the command into
+ the output stream.
-- **intro**: *str* - if provided this serves as the intro banner printed once
- at start of application, after ``preloop`` runs
-- **allow_cli_args**: *bool* - if True (default), then searches for -t or
- --test at command line to invoke transcript testing mode instead of a normal
- main loop and also processes any commands provided as arguments on the
- command line just prior to entering the main loop
-- **echo**: *bool* - if True, then the command line entered is echoed to the
- screen (most useful when running scripts)
-- **prompt**: *str* - sets the prompt which is displayed, can be dynamically
- changed based on application state and/or command results
+In addition, several arguments to :meth:`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.
+- ``transcript_files`` - see :ref:`features/transcripts:Transcripts` for more
+ information
+- ``startup_script`` - run a script on initialization. See
+ :ref:`features/scripting:Scripting` for more information.
Command Processing Loop
-----------------------
-When you call `.cmdloop()`, the following sequence of events are repeated until
-the application exits:
+When you call :meth:`cmd2.Cmd.cmdloop`, the following sequence of events are
+repeated until the application exits:
#. Output the prompt
#. Accept user input
-#. Parse user input into `Statement` object
-#. Call methods registered with `register_postparsing_hook()`
+#. Parse user input into a :class:`~cmd2.Statement` object
+#. Call methods registered with :meth:`~cmd2.Cmd.register_postparsing_hook()`
#. Redirect output, if user asked for it and it's allowed
#. Start timer
-#. Call methods registered with `register_precmd_hook()`
-#. Call `precmd()` - for backwards compatibility with ``cmd.Cmd``
-#. Add statement to history
+#. Call methods registered with :meth:`~cmd2.Cmd.register_precmd_hook`
+#. Call :meth:`~cmd2.Cmd.precmd` - for backwards compatibility with ``cmd.Cmd``
+#. Add statement to :ref:`features/history:History`
#. Call `do_command` method
-#. Call methods registered with `register_postcmd_hook()`
-#. Call `postcmd(stop, statement)` - for backwards compatibility with
+#. Call methods registered with :meth:`~cmd2.Cmd.register_postcmd_hook()`
+#. Call :meth:`~cmd2.Cmd.postcmd` - for backwards compatibility with
``cmd.Cmd``
#. Stop timer and display the elapsed time
#. Stop redirecting output if it was redirected
-#. Call methods registered with `register_cmdfinalization_hook()`
+#. Call methods registered with
+ :meth:`~cmd2.Cmd.register_cmdfinalization_hook()`
By registering hook methods, steps 4, 8, 12, and 16 allow you to run code
during, and control the flow of the command processing loop. Be aware that
@@ -103,6 +122,7 @@ Postparsing, precommand, and postcommand hook methods share some common ways to
influence the command processing loop.
If a hook raises an exception:
+
- no more hooks (except command finalization hooks) of any kind will be called
- if the command has not yet been executed, it will not be executed
- the exception message will be displayed for the user.
@@ -136,35 +156,37 @@ To define and register a postparsing hook, do the following::
# is available as params.statement
return params
-``register_postparsing_hook()`` checks the method signature of the passed
-callable, and raises 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``.
+:meth:`~cmd2.Cmd.register_postparsing_hook` checks the method signature of the
+passed callable, and raises 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 ``PostparsingData`` object
-which we will refer to as ``params``. ``params`` contains two attributes.
-``params.statement`` is a ``Statement`` object which describes the parsed
-user input. There are many useful attributes in the ``Statement``
+The hook method will be passed one parameter, a
+:class:`~cmd2.plugin.PostparsingData` object which we will refer to as
+``params``. ``params`` contains two attributes. ``params.statement`` is a
+:class:`~cmd2.Statement` object which describes the parsed user input.
+There are many useful attributes in the :class:`~cmd2.Statement`
object, including ``.raw`` which contains exactly what the user typed.
``params.stop`` is set to ``False`` by default.
-The hook method must return a ``PostparsingData`` object, and it is very
-convenient to just return the object passed into the hook method. The hook
-method may modify the attributes of the object to influece the behavior of
-the application. If ``params.stop`` is set to true, a fatal failure is
-triggered prior to execution of the command, and the application exits.
-
-To modify the user input, you create a new ``Statement`` object and return it
-in ``params.statement``. Don't try and directly modify the contents of a
-``Statement`` object, there be dragons. Instead, use the various attributes in
-a ``Statement`` object to construct a new string, and then parse that string to
-create a new ``Statement`` object.
-
-``cmd2.Cmd()`` uses an instance of ``cmd2.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 ``self.statement_parser`` attribute. Here's a simple example
-which shows the proper technique::
+The hook method must return a :class:`cmd2.plugin.PostparsingData` object, and
+it is very convenient to just return the object passed into the hook method.
+The hook method may modify the attributes of the object to influece the
+behavior of the application. If ``params.stop`` is set to true, a fatal failure
+is triggered prior to execution of the command, and the application exits.
+
+To modify the user input, you create a new :class:`~cmd2.Statement` object and
+return it in ``params.statement``. Don't try and directly modify the contents
+of a :class:`~cmd2.Statement` object, there be dragons. Instead, use the
+various attributes in a :class:`~cmd2.Statement` object to construct a new
+string, and then parse that string to create a new :class:`~cmd2.Statement`
+object.
+
+:class:`cmd2.Cmd` uses an instance of :class:`~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 :data:`~cmd2.Cmd.statement_parser` attribute.
+Here's a simple example which shows the proper technique::
def myhookmethod(self, params: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
if not '|' in params.statement.raw:
@@ -172,10 +194,11 @@ which shows the proper technique::
params.statement = self.statement_parser.parse(newinput)
return params
-If a postparsing hook returns a ``PostparsingData`` object with the ``stop``
-attribute set to ``True``:
+If a postparsing hook returns a :class:`~cmd2.plugin.PostparsingData` object
+with the :data:`~cmd2.plugin.PostparsingData.stop` attribute set to ``True``:
-- no more hooks of any kind (except command finalization hooks) will be called
+- no more hooks of any kind (except
+ :ref:`features/hooks:Command Finalization Hooks`) will be called
- the command will not be executed
- no error message will be displayed to the user
- the application will exit
@@ -189,7 +212,7 @@ terminate. If your hook needs to be able to exit the application, you should
implement it as a postparsing hook.
Once output is redirected and the timer started, all the hooks registered with
-``register_precmd_hook()`` are called. Here's how to do it::
+:meth:`~cmd2.Cmd.register_precmd_hook` are called. Here's how to do it::
class App(cmd2.Cmd):
def __init__(self, *args, *kwargs):
@@ -201,23 +224,23 @@ Once output is redirected and the timer started, all the hooks registered with
# is available as data.statement
return data
-``register_precmd_hook()`` checks the method signature of the passed callable,
-and raises a ``TypeError`` if it has the wrong number of parameters. It will
-also raise a ``TypeError`` if the parameters and return value are not annotated
-as ``PrecommandData``.
+:meth:`~cmd2.Cmd.register_precmd_hook` checks the method signature of the
+passed callable, and raises a ``TypeError`` if it has the wrong number of
+parameters. It will also raise a ``TypeError`` if the parameters and return
+value are not annotated as ``PrecommandData``.
-You may choose to modify the user input by creating a new ``Statement`` with
-different properties (see above). If you do so, assign your new ``Statement``
-object to ``data.statement``.
+You may choose to modify the user input by creating a new
+:class:`~cmd2.Statement` with different properties (see above). If you do so,
+assign your new :class:`~cmd2.Statement` object to ``data.statement``.
-The precommand hook must return a ``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 :class:`~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,
-``self.precmd(statement)`` will be called. To retain full backward
-compatibility with ``cmd.Cmd``, this method is passed a ``Statement``, not a
-``PrecommandData`` object.
+:meth:`~cmd2.Cmd.precmd` will be called. To retain full backward compatibility
+with ``cmd.Cmd``, this method is passed a :class:`~cmd2.Statement`, not a
+:class:`~cmd2.plugin.PrecommandData` object.
Postcommand Hooks
@@ -238,27 +261,29 @@ Here's how to define and register a postcommand hook::
def myhookmethod(self, data: cmd2.plugin.PostcommandData) -> cmd2.plugin.PostcommandData:
return data
-Your hook will be passed a ``PostcommandData`` object, which has a
-``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.
+Your hook will be passed a :class:`~cmd2.plugin.PostcommandData` object, which
+has a :data:`~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.
If any postcommand hook raises an exception, the exception will be displayed to
the user, and no further postcommand hook methods will be called. Command
finalization hooks, if any, will be called.
After all registered postcommand hooks have been called,
-``self.postcmd(statement)`` will be called to retain full backward
-compatibility with ``cmd.Cmd``.
-
-If any postcommand hook (registered or ``self.postcmd()``) returns a
-``PostcommandData`` object with the stop attribute set to ``True``, subsequent
-postcommand hooks will still be called, as will the command finalization hooks,
-but once those hooks have all been called, the application will terminate.
-Likewise, if ``self.postcmd()`` returns ``True``, the command finalization
-hooks will be called before the application terminates.
-
-Any postcommand hook can change the value of the ``stop`` parameter before
+``self.postcmd`` will be called to retain full backward compatibility
+with ``cmd.Cmd``.
+
+If any postcommand hook (registered or ``self.postcmd``) returns a
+:class:`~cmd2.plugin.PostcommandData` object with the stop attribute set to
+``True``, subsequent postcommand hooks will still be called, as will the
+command finalization hooks, but once those hooks have all been called, the
+application will terminate. Likewise, if :``self.postcmd`` returns
+``True``, the command finalization hooks will be called before the application
+terminates.
+
+Any postcommand hook can change the value of the ``stop`` attribute before
returning it, and the modified value will be passed to the next postcommand
hook. The value returned by the final postcommand hook will be passed to the
command finalization hooks, which may further modify the value. If your hook
@@ -279,20 +304,24 @@ command finalization hook::
super().__init__(*args, **kwargs)
self.register_cmdfinalization_hook(self.myhookmethod)
- def myhookmethod(self, stop, statement):
+ def myhookmethod(self, data: cmd2.plugin.CommandFinalizationData) -> cmd2.plugin.CommandFinalizationData:
return stop
-Command Finalization hooks must check whether the statement object is ``None``.
-There are certain circumstances where these hooks may be called before the
-statement has been parsed, so you can't always rely on having a statement.
+Command Finalization hooks must check whether the
+:data:`~cmd2.plugin.CommandFinalizationData.statement` attribute of the passed
+:class:`~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
+:data:`~cmd2.plugin.CommandFinalizationData.statement`.
If any prior postparsing or precommand hook has requested the application to
-terminate, the value of the ``stop`` parameter passed to the first command
-finalization hook will be ``True``. Any command finalization hook can change
-the value of the ``stop`` parameter before returning it, and the modified value
-will be passed to the next command finalization hook. The value returned by the
-final command finalization hook will determine whether the application
-terminates or not.
+terminate, the value of the :data:`~cmd2.plugin.CommandFinalizationData.stop`
+attribute passed to the first command finalization hook will be ``True``. Any
+command finalization hook can change the value of the
+:data:`~cmd2.plugin.CommandFinalizationData.stop` attribute before returning
+it, and the modified value will be passed to the next command finalization
+hook. The value returned by the final command finalization hook will determine
+whether the application terminates or not.
This approach to command finalization hooks can be powerful, but it can also
cause problems. If your hook blindly returns ``False``, a prior hook's requst
diff --git a/docs/features/plugins.rst b/docs/features/plugins.rst
index 00c0a9f0..ecd3a32d 100644
--- a/docs/features/plugins.rst
+++ b/docs/features/plugins.rst
@@ -5,18 +5,15 @@ Plugins
a ``cmd2`` plugin which can extend basic ``cmd2`` functionality and can be
used by multiple applications.
-Adding functionality
---------------------
-
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`` project will inject the plugin's code into their subclass of
+:class:`cmd2.Cmd`.
Mixin and Initialization
-~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------
The following short example shows how to mix in a plugin and how the plugin
gets initialized.
@@ -29,7 +26,6 @@ Here's the plugin::
super().__init__(*args, **kwargs)
# code placed here runs after cmd2.Cmd initializes
-
and an example app which uses the plugin::
import cmd2
@@ -44,22 +40,24 @@ and an example app which uses the plugin::
# code placed here runs after cmd2.Cmd and
# all plugins have initialized
-Note how the plugin must be inherited (or mixed in) before ``cmd2.Cmd``.
+Note how the plugin must be inherited (or mixed in) before :class:`cmd2.Cmd`.
This is required for two reasons:
-- The ``cmd.Cmd.__init__()`` method in the python standard library does not
+- 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.
+- You may want your plugin to be able to override methods from
+ :class:`cmd2.Cmd`. If you mixin the plugin after ``cmd2.Cmd``, the python
+ method resolution order will call :class:`cmd2.Cmd` methods before it calls
+ those in your plugin.
+
Add commands
-~~~~~~~~~~~~
+------------
Your plugin can add user visible commands. You do it the same way in a plugin
-that you would in a ``cmd2.Cmd`` app::
+that you would in a :class:`cmd2.Cmd` app::
class MyPlugin:
def do_say(self, statement):
@@ -67,11 +65,12 @@ that you would in a ``cmd2.Cmd`` app::
self.poutput(statement)
You have all the same capabilities within the plugin that you do inside a
-``cmd2.Cmd`` app, including argument parsing via decorators and custom help
-methods.
+:class:`cmd2.Cmd` app, including argument parsing via decorators and custom
+help methods.
+
Add (or hide) settings
-~~~~~~~~~~~~~~~~~~~~~~
+----------------------
A plugin may add user controllable settings to the application. Here's an
example::
@@ -84,33 +83,38 @@ example::
self.mysetting = 'somevalue'
self.add_settable(cmd2.Settable('mysetting', str, 'short help message for mysetting'))
-You can also hide settings from the user by removing them from
-``self.settables``.
+You can hide settings from the user by calling
+:meth:`~cmd2.Cmd.remove_settable`. See :ref:`features/settings:Settings` for
+more information.
+
Decorators
-~~~~~~~~~~
+----------
Your plugin can provide a decorator which users of your plugin can use to
wrap functionality around their own commands.
+
Override methods
-~~~~~~~~~~~~~~~~
+----------------
-Your plugin can override core ``cmd2.Cmd`` methods, changing their behavior.
-This approach should be used sparingly, because it is very brittle. If a
-developer chooses to use multiple plugins in their application, and several
-of the plugins override the same method, only the first plugin to be mixed in
-will have the overridden method called.
+Your plugin can override core :class:`cmd2.Cmd` methods, changing their
+behavior. This approach should be used sparingly, because it is very brittle.
+If a developer chooses to use multiple plugins in their application, and
+several of the plugins override the same method, only the first plugin to be
+mixed in will have the overridden method called.
Hooks are a much better approach.
+
Hooks
-~~~~~
+-----
-Plugins can register hooks, 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 :ref:`features/hooks:Hooks` section.
+Plugins can register hook methods, which are called by :class:`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 :ref:`features/hooks:Hooks`
+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
@@ -141,7 +145,7 @@ ways hooks can influence the lifecycle.
Classes and Functions
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
Your plugin can also provide classes and functions which can be used by
developers of ``cmd2`` based applications. Describe these classes and
@@ -152,6 +156,4 @@ available.
Examples
--------
-.. _cmd2_plugin_template: https://github.com/python-cmd2/cmd2-plugin-template
-
-See cmd2_plugin_template_ for more info.
+See `<https://github.com/python-cmd2/cmd2-plugin-template>`_ for more info.
diff --git a/docs/features/transcripts.rst b/docs/features/transcripts.rst
index 1af2a74f..fa6d9cb3 100644
--- a/docs/features/transcripts.rst
+++ b/docs/features/transcripts.rst
@@ -185,9 +185,10 @@ output matches the expected result from the transcript.
.. note::
- If you have set ``allow_cli_args`` to False in order to disable parsing of
- command line arguments at invocation, then the use of ``-t`` or ``--test`` to
- run transcript testing is automatically disabled. In this case, you can
+ If you have passed an ``allow_cli_args`` parameter containing `False` to
+ :meth:`cmd2.Cmd.__init__` in order to disable parsing of command line
+ arguments at invocation, then the use of ``-t`` or ``--test`` to run
+ transcript testing is automatically disabled. In this case, you can
alternatively provide a value for the optional ``transcript_files`` when
constructing the instance of your ``cmd2.Cmd`` derived class in order to
cause a transcript test to run::
diff --git a/docs/overview/integrating.rst b/docs/overview/integrating.rst
index 0cc7816e..db5cb206 100644
--- a/docs/overview/integrating.rst
+++ b/docs/overview/integrating.rst
@@ -10,8 +10,11 @@ include ``cmd2``. Make sure your ``setup.py`` includes the following::
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. We recommend that you follow the advice given by the Python
-Packaging User Guide related to `install_requires
+version number. The public API is documented in the :ref:`api/index:API
+Reference`.
+
+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 setting an upper bound on the allowed version, you can ensure that your
project does not inadvertently get installed with an incompatible future