summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--cmd2/cmd2.py1
-rw-r--r--cmd2/decorators.py8
-rw-r--r--tests_isolated/test_commandset/test_commandset.py43
4 files changed, 51 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e32dd4af..0f231fec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.3.2 (November 22, 2021)
+* Bug Fixes
+ * Fixed issue where a `ns_provider` could be passed `None` instead of its correct `cmd2.Cmd` or `CommandSet` value.
+
## 2.3.1 (November 18, 2021)
* Bug Fixes
* Fixed issue introduced in 2.3.0 with `AlternatingTable`, `BorderedTable`, and `SimpleTable` that caused
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 09b3fe43..b36d40bc 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -5418,7 +5418,6 @@ class Cmd(cmd.Cmd):
:param cmd_support_func: command support function. This could be a completer or namespace provider
:param cmd_self: The `self` associated with the command or subcommand
- :return:
"""
# figure out what class the command support function was defined in
func_class: Optional[Type[Any]] = get_defining_class(cmd_support_func)
diff --git a/cmd2/decorators.py b/cmd2/decorators.py
index 1ff0bdbe..c06142fb 100644
--- a/cmd2/decorators.py
+++ b/cmd2/decorators.py
@@ -276,9 +276,9 @@ def with_argparser(
with the given instance of argparse.ArgumentParser.
: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 ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet 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
:param with_unknown_args: if true, then capture unknown args
:return: function that gets passed argparse-parsed args in a ``Namespace``
@@ -351,7 +351,7 @@ def with_argparser(
# functions are registered with the command argparser before anything is instantiated, we
# need to find an instance at runtime that matches the types during declaration
provider_self = cmd2_app._resolve_func_self(ns_provider, args[0])
- namespace = ns_provider(provider_self if not None else cmd2_app)
+ namespace = ns_provider(provider_self if provider_self is not None else cmd2_app)
try:
new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]]
diff --git a/tests_isolated/test_commandset/test_commandset.py b/tests_isolated/test_commandset/test_commandset.py
index e9fba114..522a99e1 100644
--- a/tests_isolated/test_commandset/test_commandset.py
+++ b/tests_isolated/test_commandset/test_commandset.py
@@ -1104,3 +1104,46 @@ Parameter 'arbitrary_value' not supported (type 'set' for list of parameters).
settable_attrib_name='some_value',
)
)
+
+
+class NsProviderSet(cmd2.CommandSet):
+ # CommandSet which implements a namespace provider
+ def __init__(self, dummy):
+ # Use dummy argument so this won't be autoloaded by other tests
+ super(NsProviderSet, self).__init__()
+
+ def ns_provider(self) -> argparse.Namespace:
+ ns = argparse.Namespace()
+ # Save what was passed as self from with_argparser().
+ ns.self = self
+ return ns
+
+
+class NsProviderApp(cmd2.Cmd):
+ # Used to test namespace providers in CommandSets
+ def __init__(self, *args, **kwargs) -> None:
+ super().__init__(*args, **kwargs)
+ super(NsProviderApp, self).__init__(*args, **kwargs)
+
+ @cmd2.with_argparser(cmd2.Cmd2ArgumentParser(), ns_provider=NsProviderSet.ns_provider)
+ def do_test_ns(self, args: argparse.Namespace) -> None:
+ # Save args.self so the unit tests can read it.
+ self.last_result = args.self
+
+
+def test_ns_provider():
+ """This exercises code in with_argparser() decorator that calls namespace providers"""
+ ns_provider_set = NsProviderSet(1)
+ app = NsProviderApp(auto_load_commands=False)
+
+ # First test the case in which a namespace provider function resides in a CommandSet class which is registered.
+ # with_argparser() will pass the CommandSet instance to the ns_provider() function.
+ app.register_command_set(ns_provider_set)
+ run_cmd(app, "test_ns")
+ assert app.last_result == ns_provider_set
+
+ # Now test the case in which a namespace provider function resides in a CommandSet class which is not registered.
+ # with_argparser() will receive None from cmd2.Cmd._resolve_func_self() and therefore pass app as self to ns_provider().
+ app.unregister_command_set(ns_provider_set)
+ run_cmd(app, "test_ns")
+ assert app.last_result == app