diff options
-rw-r--r-- | cmd2/cmd2.py | 4 | ||||
-rw-r--r-- | docs/argument_processing.rst | 16 | ||||
-rwxr-xr-x | examples/table_display.py | 26 |
3 files changed, 33 insertions, 13 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 4502c53a..c000fb80 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -193,7 +193,7 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, preserve """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 argparser: given instance of ArgumentParser + :param argparser: unique instance of ArgumentParser :param preserve_quotes: if True, then the arguments passed to arparse be maintain their quotes :return: function that gets passed parsed args and a list of unknown args """ @@ -234,7 +234,7 @@ def with_argparser(argparser: argparse.ArgumentParser, preserve_quotes: bool=Fal """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given instance of argparse.ArgumentParser. - :param argparser: given instance of ArgumentParser + :param argparser: unique instance of ArgumentParser :param preserve_quotes: if True, then the arguments passed to arparse be maintain their quotes :return: function that gets passed parsed args """ diff --git a/docs/argument_processing.rst b/docs/argument_processing.rst index 8aed7498..9d13a7c8 100644 --- a/docs/argument_processing.rst +++ b/docs/argument_processing.rst @@ -25,10 +25,10 @@ Using the argument parser decorator =================================== For each command in the ``cmd2`` subclass which requires argument parsing, -create an instance of ``argparse.ArgumentParser()`` which can parse the +create a unique instance of ``argparse.ArgumentParser()`` which can parse the input appropriately for the command. Then decorate the command method with the ``@with_argparser`` decorator, passing the argument parser as the -first parameter to the decorator. This changes the second argumen to the command method, which will contain the results +first parameter to the decorator. This changes the second argument to the command method, which will contain the results of ``ArgumentParser.parse_args()``. Here's what it looks like:: @@ -54,6 +54,16 @@ Here's what it looks like:: for i in range(min(repetitions, self.maxrepeats)): self.poutput(arg) +.. warning:: + + It is important that each command which uses the ``@with_argparser`` decorator be passed a unique instance of a + parser. This limitation is due to bugs in CPython prior to Python 3.7 which make it impossible to make a deep copy + of an instance of a ``argparse.ArgumentParser``. + + See the table_display_ example for a work-around that demonstrates how to create a function which returns a unique + instance of the parser you want. + + .. note:: The ``@with_argparser`` decorator sets the ``prog`` variable in @@ -61,6 +71,8 @@ Here's what it looks like:: This will override anything you specify in ``prog`` variable when creating the argument parser. +.. _table_display: https://github.com/python-cmd2/cmd2/blob/master/examples/table_display.py + Help Messages ============= diff --git a/examples/table_display.py b/examples/table_display.py index 63447377..7541e548 100755 --- a/examples/table_display.py +++ b/examples/table_display.py @@ -11,7 +11,6 @@ You can quit out of the pager by typing "q". You can also search for text withi WARNING: This example requires the tableformatter module: https://github.com/python-tableformatter/tableformatter - pip install tableformatter """ -import argparse from typing import Tuple import cmd2 @@ -142,6 +141,21 @@ def high_density_objs(row_obj: CityInfo) -> dict: return opts +def make_table_parser() -> cmd2.argparse_completer.ACArgumentParser: + """Create a unique instance of an argparse Argument parser for processing table arguments. + + NOTE: The two cmd2 argparse decorators require that each parser be unique, even if they are essentially a deep copy + of each other. For cases like that, you can create a function to return a unique instance of a parser, which is + what is being done here. + """ + table_parser = cmd2.argparse_completer.ACArgumentParser() + table_item_group = table_parser.add_mutually_exclusive_group() + table_item_group.add_argument('-c', '--color', action='store_true', help='Enable color') + table_item_group.add_argument('-f', '--fancy', action='store_true', help='Fancy Grid') + table_item_group.add_argument('-s', '--sparse', action='store_true', help='Sparse Grid') + return table_parser + + class TableDisplay(cmd2.Cmd): """Example cmd2 application showing how you can display tabular data.""" @@ -169,18 +183,12 @@ class TableDisplay(cmd2.Cmd): formatted_table = tf.generate_table(rows=rows, columns=columns, grid_style=grid, row_tagger=row_stylist) self.ppaged(formatted_table, chop=True) - table_parser = argparse.ArgumentParser() - table_item_group = table_parser.add_mutually_exclusive_group() - table_item_group.add_argument('-c', '--color', action='store_true', help='Enable color') - table_item_group.add_argument('-f', '--fancy', action='store_true', help='Fancy Grid') - table_item_group.add_argument('-s', '--sparse', action='store_true', help='Sparse Grid') - - @cmd2.with_argparser(table_parser) + @cmd2.with_argparser(make_table_parser()) def do_table(self, args): """Display data in iterable form on the Earth's most populated cities in a table.""" self.ptable(EXAMPLE_ITERABLE_DATA, COLUMNS, args, high_density_tuples) - @cmd2.with_argparser(table_parser) + @cmd2.with_argparser(make_table_parser()) def do_object_table(self, args): """Display data in object form on the Earth's most populated cities in a table.""" self.ptable(EXAMPLE_OBJECT_DATA, OBJ_COLS, args, high_density_objs) |