Skip to content

Commit 8e9a21c

Browse files
committed
Fixed issue where a ns_provider could be passed None instead of its correct cmd2.Cmd or CommandSet value.
1 parent 087c206 commit 8e9a21c

File tree

4 files changed

+51
-5
lines changed

4 files changed

+51
-5
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.3.2 (November 22, 2021)
2+
* Bug Fixes
3+
* Fixed issue where a `ns_provider` could be passed `None` instead of its correct `cmd2.Cmd` or `CommandSet` value.
4+
15
## 2.3.1 (November 18, 2021)
26
* Bug Fixes
37
* Fixed issue introduced in 2.3.0 with `AlternatingTable`, `BorderedTable`, and `SimpleTable` that caused

cmd2/cmd2.py

-1
Original file line numberDiff line numberDiff line change
@@ -5418,7 +5418,6 @@ def _resolve_func_self(
54185418
54195419
:param cmd_support_func: command support function. This could be a completer or namespace provider
54205420
:param cmd_self: The `self` associated with the command or subcommand
5421-
:return:
54225421
"""
54235422
# figure out what class the command support function was defined in
54245423
func_class: Optional[Type[Any]] = get_defining_class(cmd_support_func)

cmd2/decorators.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ def with_argparser(
276276
with the given instance of argparse.ArgumentParser.
277277
278278
:param parser: unique instance of ArgumentParser
279-
:param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an
280-
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
281-
state data that affects parsing.
279+
:param ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an
280+
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that
281+
affects parsing.
282282
:param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
283283
:param with_unknown_args: if true, then capture unknown args
284284
:return: function that gets passed argparse-parsed args in a ``Namespace``
@@ -351,7 +351,7 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
351351
# functions are registered with the command argparser before anything is instantiated, we
352352
# need to find an instance at runtime that matches the types during declaration
353353
provider_self = cmd2_app._resolve_func_self(ns_provider, args[0])
354-
namespace = ns_provider(provider_self if not None else cmd2_app)
354+
namespace = ns_provider(provider_self if provider_self is not None else cmd2_app)
355355

356356
try:
357357
new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]]

tests_isolated/test_commandset/test_commandset.py

+43
Original file line numberDiff line numberDiff line change
@@ -1104,3 +1104,46 @@ def __init__(self):
11041104
settable_attrib_name='some_value',
11051105
)
11061106
)
1107+
1108+
1109+
class NsProviderSet(cmd2.CommandSet):
1110+
# CommandSet which implements a namespace provider
1111+
def __init__(self, dummy):
1112+
# Use dummy argument so this won't be autoloaded by other tests
1113+
super(NsProviderSet, self).__init__()
1114+
1115+
def ns_provider(self) -> argparse.Namespace:
1116+
ns = argparse.Namespace()
1117+
# Save what was passed as self from with_argparser().
1118+
ns.self = self
1119+
return ns
1120+
1121+
1122+
class NsProviderApp(cmd2.Cmd):
1123+
# Used to test namespace providers in CommandSets
1124+
def __init__(self, *args, **kwargs) -> None:
1125+
super().__init__(*args, **kwargs)
1126+
super(NsProviderApp, self).__init__(*args, **kwargs)
1127+
1128+
@cmd2.with_argparser(cmd2.Cmd2ArgumentParser(), ns_provider=NsProviderSet.ns_provider)
1129+
def do_test_ns(self, args: argparse.Namespace) -> None:
1130+
# Save args.self so the unit tests can read it.
1131+
self.last_result = args.self
1132+
1133+
1134+
def test_ns_provider():
1135+
"""This exercises code in with_argparser() decorator that calls namespace providers"""
1136+
ns_provider_set = NsProviderSet(1)
1137+
app = NsProviderApp(auto_load_commands=False)
1138+
1139+
# First test the case in which a namespace provider function resides in a CommandSet class which is registered.
1140+
# with_argparser() will pass the CommandSet instance to the ns_provider() function.
1141+
app.register_command_set(ns_provider_set)
1142+
run_cmd(app, "test_ns")
1143+
assert app.last_result == ns_provider_set
1144+
1145+
# Now test the case in which a namespace provider function resides in a CommandSet class which is not registered.
1146+
# with_argparser() will receive None from cmd2.Cmd._resolve_func_self() and therefore pass app as self to ns_provider().
1147+
app.unregister_command_set(ns_provider_set)
1148+
run_cmd(app, "test_ns")
1149+
assert app.last_result == app

0 commit comments

Comments
 (0)