summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/conf.py13
-rw-r--r--docs/features/argument_processing.rst35
-rwxr-xr-xexamples/help_categories.py29
3 files changed, 71 insertions, 6 deletions
diff --git a/docs/conf.py b/docs/conf.py
index cd320f52..9bb2e855 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -169,3 +169,16 @@ intersphinx_mapping = {'http://docs.python.org/': None}
autodoc_default_options = {
'member-order': 'bysource'
}
+
+# Ignore nitpicky warnings from autodoc which are occurring for very new versions of Sphinx and autodoc
+# They seem to be happening because autodoc is now trying to add hyperlinks to docs for typehint classes
+nitpick_ignore = [
+ ('py:class', 'Callable[[None], None]'),
+ ('py:class', 'cmd2.cmd2.Cmd'),
+ ('py:class', 'cmd2.parsing.Statement'),
+ ('py:class', 'IO'),
+ ('py:class', 'None'),
+ ('py:class', 'Optional[Callable[[...], argparse.Namespace]]'),
+ ('py:class', 'TextIO'),
+ ('py:class', 'Union[None, Iterable, Callable]'),
+]
diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst
index 204c2876..e8e5457d 100644
--- a/docs/features/argument_processing.rst
+++ b/docs/features/argument_processing.rst
@@ -347,3 +347,38 @@ class which inherits from ``argparse.ArgumentParser`` and improves error and
help output.
+Decorator Order
+---------------
+
+If you are using custom decorators in combination with either
+``@cmd2.with_argparser`` or ``@cmd2.with_argparser_and_unknown_args``, then the
+order of your custom decorator(s) relative to the ``cmd2`` decorator matters
+when it comes to runtime behavior and ``argparse`` errors. There is nothing
+``cmd2``-specific here, this is just a side-effect of how decorators work in
+Python. To learn more about how decorators work, see decorator_primer_.
+
+If you want your custom decorator's runtime behavior to occur in the case of
+an ``argparse`` error, then that decorator needs to go **after** the
+``argparse`` one, e.g.::
+
+ @cmd2.with_argparser(foo_parser)
+ @my_decorator
+ def do_foo(self, args: argparse.Namespace) -> None:
+ """foo docs"""
+ pass
+
+However, if you do NOT want the customer decorator runtime behavior to occur
+even in the case of an `argparse` error, then that decorator needs to go
+**before** the ``arpgarse`` one, e.g.::
+
+ @my_decorator
+ @cmd2.with_argparser(bar_parser)
+ def do_bar(self, args: argparse.Namespace) -> None:
+ """bar docs"""
+ pass
+
+The help_categories_ example demonstrates both above cases in a concrete
+fashion.
+
+.. _decorator_primer: https://realpython.com/primer-on-python-decorators
+.. _help_categories: https://github.com/python-cmd2/cmd2/blob/master/examples/help_categories.py
diff --git a/examples/help_categories.py b/examples/help_categories.py
index 602bf441..7401bafe 100755
--- a/examples/help_categories.py
+++ b/examples/help_categories.py
@@ -2,17 +2,28 @@
# coding=utf-8
"""
A sample application for tagging categories on commands.
-"""
-import argparse
+It also demonstrates the effects of decorator order when it comes to argparse errors occurring.
+"""
+import functools
import cmd2
from cmd2 import COMMAND_NAME
+def my_decorator(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwds):
+ print('Calling decorated function')
+ return f(*args, **kwds)
+ return wrapper
+
+
class HelpCategories(cmd2.Cmd):
""" Example cmd2 application. """
+ START_TIMES = ['now', 'later', 'sometime', 'whenever']
+
# Command categories
CMD_CAT_CONNECTING = 'Connecting'
CMD_CAT_APP_MGMT = 'Application Management'
@@ -42,6 +53,11 @@ class HelpCategories(cmd2.Cmd):
"""Deploy command"""
self.poutput('Deploy')
+ start_parser = cmd2.DEFAULT_ARGUMENT_PARSER(description='Start', epilog='my_decorator runs even with argparse errors')
+ start_parser.add_argument('when', choices=START_TIMES, help='Specify when to start')
+
+ @my_decorator
+ @cmd2.with_argparser(start_parser)
def do_start(self, _):
"""Start command"""
self.poutput('Start')
@@ -54,13 +70,13 @@ class HelpCategories(cmd2.Cmd):
"""Redeploy command"""
self.poutput('Redeploy')
- restart_parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
- restart_parser.add_argument('when', default='now',
- choices=['now', 'later', 'sometime', 'whenever'],
- help='Specify when to restart')
+ restart_parser = cmd2.DEFAULT_ARGUMENT_PARSER(description='Restart',
+ epilog='my_decorator does not run when argparse errors')
+ restart_parser.add_argument('when', choices=START_TIMES, help='Specify when to restart')
@cmd2.with_argparser(restart_parser)
@cmd2.with_category(CMD_CAT_APP_MGMT)
+ @my_decorator
def do_restart(self, _):
"""Restart command"""
self.poutput('Restart')
@@ -158,5 +174,6 @@ class HelpCategories(cmd2.Cmd):
if __name__ == '__main__':
import sys
+
c = HelpCategories()
sys.exit(c.cmdloop())