Skip to content

Commit b418f0f

Browse files
committed
The functions cmd2 adds to Namespaces (get_statement() and get_handler()) are now Cmd2AttributeWrapper objects.
This makes it easy to filter out which attributes in an argparse.Namespace were added by cmd2.
1 parent 97c348c commit b418f0f

File tree

6 files changed

+39
-16
lines changed

6 files changed

+39
-16
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.3.6 (August 26, 2020)
2+
* Enhancements
3+
* The functions cmd2 adds to Namespaces (`get_statement()` and `get_handler()`) are now
4+
`Cmd2AttributeWrapper` objects. This makes it easy to filter out which attributes in an
5+
`argparse.Namespace` were added by `cmd2`.
6+
* Deprecations
7+
* ``Namespace.__statement__`` will be removed in `cmd2` 2.0.0. Use `Namespace.get_statement()` going forward.
8+
19
## 1.3.5 (August 25, 2020)
210
* Bug Fixes
311
* Fixed `RecursionError` when printing an `argparse.Namespace` caused by custom attribute cmd2 was adding

cmd2/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
pass
1717

1818
from .ansi import style, fg, bg
19-
from .argparse_custom import Cmd2ArgumentParser, CompletionItem, set_default_argument_parser
19+
from .argparse_custom import Cmd2ArgumentParser, Cmd2AttributeWrapper, CompletionItem, set_default_argument_parser
2020

2121
# Check if user has defined a module that sets a custom value for argparse_custom.DEFAULT_ARGUMENT_PARSER
2222
import argparse

cmd2/argparse_custom.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def my_completer_method(self, text, line, begidx, endidx, arg_tokens)
221221
import sys
222222
# noinspection PyUnresolvedReferences,PyProtectedMember
223223
from argparse import ONE_OR_MORE, ZERO_OR_MORE, ArgumentError, _
224-
from typing import Callable, Optional, Tuple, Type, Union
224+
from typing import Any, Callable, Optional, Tuple, Type, Union
225225

226226
from . import ansi, constants
227227

@@ -904,6 +904,18 @@ def _print_message(self, message, file=None):
904904
ansi.style_aware_write(file, message)
905905

906906

907+
class Cmd2AttributeWrapper:
908+
"""
909+
Wraps a cmd2-specific attribute added to an argparse Namespace. This class is
910+
callable because the argparse decorator adds these as getter functions to Namespaces.
911+
"""
912+
def __init__(self, attribute: Any):
913+
self.__attribute = attribute
914+
915+
def __call__(self) -> Any:
916+
return self.__attribute
917+
918+
907919
# The default ArgumentParser class for a cmd2 app
908920
DEFAULT_ARGUMENT_PARSER = Cmd2ArgumentParser
909921

cmd2/decorators.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
55

66
from . import constants
7+
from .argparse_custom import Cmd2AttributeWrapper
78
from .exceptions import Cmd2ArgparseError
89
from .parsing import Statement
910

@@ -186,10 +187,9 @@ def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *,
186187
needs to be prepopulated with state data that affects parsing.
187188
:param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
188189
:return: function that gets passed argparse-parsed args in a ``Namespace`` and a list
189-
of unknown argument strings. A member called ``__statement__`` is added to the
190+
of unknown argument strings. A function called ``get_statement()`` is added to the
190191
``Namespace`` to provide command functions access to the :class:`cmd2.Statement`
191192
object. This can be useful if the command function needs to know the command line.
192-
``__statement__`` can also be retrieved by calling ``get_statement()`` on the ``Namespace``.
193193
194194
:Example:
195195
@@ -223,12 +223,12 @@ def with_argparser(parser: argparse.ArgumentParser, *,
223223
:param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an
224224
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
225225
state data that affects parsing.
226-
:param preserve_quotes: if True, then arguments passed to argparse maintain their quotes
226+
:param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
227227
:param with_unknown_args: if true, then capture unknown args
228-
:return: function that gets passed the argparse-parsed args in a Namespace
229-
A member called __statement__ is added to the Namespace to provide command functions access to the
230-
Statement object. This can be useful if the command function needs to know the command line.
231-
``__statement__`` can also be retrieved by calling ``get_statement()`` on the ``Namespace``.
228+
:return: function that gets passed argparse-parsed args in a ``Namespace``. A function called
229+
``get_statement()`` is added to the ``Namespace`` to provide command functions access to
230+
the :class:`cmd2.Statement` object. This can be useful if the command function needs to know
231+
the command line.
232232
233233
:Example:
234234
@@ -300,11 +300,16 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
300300
else:
301301
# Add statement to Namespace and a getter function for it
302302
setattr(ns, constants.NS_ATTR_STATEMENT, statement)
303-
setattr(ns, 'get_statement', lambda: statement)
303+
setattr(ns, 'get_statement', Cmd2AttributeWrapper(statement))
304304

305305
# Add getter function for subcmd handler, which can be None
306-
subcmd_handler = getattr(ns, constants.NS_ATTR_SUBCMD_HANDLER, None)
307-
setattr(ns, 'get_handler', lambda: subcmd_handler)
306+
handler = getattr(ns, constants.NS_ATTR_SUBCMD_HANDLER, None)
307+
setattr(ns, 'get_handler', Cmd2AttributeWrapper(handler))
308+
309+
# Remove the subcmd handler attribute from the Namespace
310+
# since get_handler() is how a developer retrieves it.
311+
if hasattr(ns, constants.NS_ATTR_SUBCMD_HANDLER):
312+
delattr(ns, constants.NS_ATTR_SUBCMD_HANDLER)
308313

309314
args_list = _arg_swap(args, statement, *new_args)
310315
return func(*args_list, **kwargs)

docs/features/argument_processing.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ handles the following for you:
1313

1414
3. Passes the resulting ``argparse.Namespace`` object to your command function.
1515
The ``Namespace`` includes the ``Statement`` object that was created when
16-
parsing the command line. It is stored in the ``__statement__`` attribute of
17-
the ``Namespace`` and can also be retrieved by calling ``get_statement()``
16+
parsing the command line. It can be retrieved by calling ``get_statement()``
1817
on the ``Namespace``.
1918

2019
4. Adds the usage message from the argument parser to your command.

tests/test_argparse.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ def do_base(self, args):
289289
func = getattr(args, 'func')
290290
func(self, args)
291291

292-
293292
# Add a subcommand using as_subcommand_to decorator
294293
has_subcmd_parser = cmd2.Cmd2ArgumentParser(description="Tests as_subcmd_to decorator")
295294
has_subcmd_subparsers = has_subcmd_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND')
@@ -306,7 +305,7 @@ def do_test_subcmd_decorator(self, args: argparse.Namespace):
306305
def subcmd_func(self, args: argparse.Namespace):
307306
# Make sure printing the Namespace works. The way we originally added get_hander()
308307
# to it resulted in a RecursionError when printing.
309-
print(args)
308+
self.poutput(args)
310309

311310
@pytest.fixture
312311
def subcommand_app():

0 commit comments

Comments
 (0)