Skip to content

Commit e018924

Browse files
committed
Added documentation stating that parsers passed to argparse decorators need to be unique
Also: - Modified table_display.py to demonstrate a workaround
1 parent a77be18 commit e018924

File tree

3 files changed

+33
-13
lines changed

3 files changed

+33
-13
lines changed

cmd2/cmd2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, preserve
193193
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given
194194
instance of argparse.ArgumentParser, but also returning unknown args as a list.
195195
196-
:param argparser: given instance of ArgumentParser
196+
:param argparser: unique instance of ArgumentParser
197197
:param preserve_quotes: if True, then the arguments passed to arparse be maintain their quotes
198198
:return: function that gets passed parsed args and a list of unknown args
199199
"""
@@ -234,7 +234,7 @@ def with_argparser(argparser: argparse.ArgumentParser, preserve_quotes: bool=Fal
234234
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
235235
with the given instance of argparse.ArgumentParser.
236236
237-
:param argparser: given instance of ArgumentParser
237+
:param argparser: unique instance of ArgumentParser
238238
:param preserve_quotes: if True, then the arguments passed to arparse be maintain their quotes
239239
:return: function that gets passed parsed args
240240
"""

docs/argument_processing.rst

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ Using the argument parser decorator
2525
===================================
2626

2727
For each command in the ``cmd2`` subclass which requires argument parsing,
28-
create an instance of ``argparse.ArgumentParser()`` which can parse the
28+
create a unique instance of ``argparse.ArgumentParser()`` which can parse the
2929
input appropriately for the command. Then decorate the command method with
3030
the ``@with_argparser`` decorator, passing the argument parser as the
31-
first parameter to the decorator. This changes the second argumen to the command method, which will contain the results
31+
first parameter to the decorator. This changes the second argument to the command method, which will contain the results
3232
of ``ArgumentParser.parse_args()``.
3333

3434
Here's what it looks like::
@@ -54,13 +54,25 @@ Here's what it looks like::
5454
for i in range(min(repetitions, self.maxrepeats)):
5555
self.poutput(arg)
5656

57+
.. warning::
58+
59+
It is important that each command which uses the ``@with_argparser`` decorator be passed a unique instance of a
60+
parser. This limitation is due to bugs in CPython prior to Python 3.7 which make it impossible to make a deep copy
61+
of an instance of a ``argparse.ArgumentParser``.
62+
63+
See the table_display_ example for a work-around that demonstrates how to create a function which returns a unique
64+
instance of the parser you want.
65+
66+
5767
.. note::
5868

5969
The ``@with_argparser`` decorator sets the ``prog`` variable in
6070
the argument parser based on the name of the method it is decorating.
6171
This will override anything you specify in ``prog`` variable when
6272
creating the argument parser.
6373

74+
.. _table_display: https://github.com/python-cmd2/cmd2/blob/master/examples/table_display.py
75+
6476

6577
Help Messages
6678
=============

examples/table_display.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
WARNING: This example requires the tableformatter module: https://github.com/python-tableformatter/tableformatter
1212
- pip install tableformatter
1313
"""
14-
import argparse
1514
from typing import Tuple
1615

1716
import cmd2
@@ -142,6 +141,21 @@ def high_density_objs(row_obj: CityInfo) -> dict:
142141
return opts
143142

144143

144+
def make_table_parser() -> cmd2.argparse_completer.ACArgumentParser:
145+
"""Create a unique instance of an argparse Argument parser for processing table arguments.
146+
147+
NOTE: The two cmd2 argparse decorators require that each parser be unique, even if they are essentially a deep copy
148+
of each other. For cases like that, you can create a function to return a unique instance of a parser, which is
149+
what is being done here.
150+
"""
151+
table_parser = cmd2.argparse_completer.ACArgumentParser()
152+
table_item_group = table_parser.add_mutually_exclusive_group()
153+
table_item_group.add_argument('-c', '--color', action='store_true', help='Enable color')
154+
table_item_group.add_argument('-f', '--fancy', action='store_true', help='Fancy Grid')
155+
table_item_group.add_argument('-s', '--sparse', action='store_true', help='Sparse Grid')
156+
return table_parser
157+
158+
145159
class TableDisplay(cmd2.Cmd):
146160
"""Example cmd2 application showing how you can display tabular data."""
147161

@@ -169,18 +183,12 @@ def ptable(self, rows, columns, grid_args, row_stylist):
169183
formatted_table = tf.generate_table(rows=rows, columns=columns, grid_style=grid, row_tagger=row_stylist)
170184
self.ppaged(formatted_table, chop=True)
171185

172-
table_parser = argparse.ArgumentParser()
173-
table_item_group = table_parser.add_mutually_exclusive_group()
174-
table_item_group.add_argument('-c', '--color', action='store_true', help='Enable color')
175-
table_item_group.add_argument('-f', '--fancy', action='store_true', help='Fancy Grid')
176-
table_item_group.add_argument('-s', '--sparse', action='store_true', help='Sparse Grid')
177-
178-
@cmd2.with_argparser(table_parser)
186+
@cmd2.with_argparser(make_table_parser())
179187
def do_table(self, args):
180188
"""Display data in iterable form on the Earth's most populated cities in a table."""
181189
self.ptable(EXAMPLE_ITERABLE_DATA, COLUMNS, args, high_density_tuples)
182190

183-
@cmd2.with_argparser(table_parser)
191+
@cmd2.with_argparser(make_table_parser())
184192
def do_object_table(self, args):
185193
"""Display data in object form on the Earth's most populated cities in a table."""
186194
self.ptable(EXAMPLE_OBJECT_DATA, OBJ_COLS, args, high_density_objs)

0 commit comments

Comments
 (0)