summaryrefslogtreecommitdiff
path: root/cmd2
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2020-02-24 17:30:34 -0500
committerGitHub <noreply@github.com>2020-02-24 17:30:34 -0500
commitd25fd7146131688e934290a3c5bf0407e904dbb2 (patch)
tree5fd4b905157eafbf3ea0071bfd625763b109ee78 /cmd2
parentfea1bc15f4a53aa72d16c2985377fe3987b6b348 (diff)
parent803f71a04d7f7290ca1390a164808679f5943178 (diff)
downloadcmd2-git-d25fd7146131688e934290a3c5bf0407e904dbb2.tar.gz
Merge pull request #899 from python-cmd2/api_docs
API documentation
Diffstat (limited to 'cmd2')
-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
10 files changed, 474 insertions, 324 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: