Skip to content

Commit 774fb39

Browse files
committed
Breaking change: Removed cmd2 app as a required second parameter to
CommandSet command functions (do_, complete_, help_). Renamed install_command_set and uninstall_command_set to register_command_set and unregister_command_set.
1 parent 4d628ea commit 774fb39

15 files changed

+229
-288
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
## 1.3.3 (TBD)
2+
* Breaking changes
3+
* CommandSet command functions (do_, complete_, help_) will no longer have the cmd2 app
4+
passed in as the first parameter after `self` since this is already a class member.
5+
* Renamed `install_command_set()` and `uninstall_command_set()` to `register_command_set()` and
6+
`unregister_command_set()` for better name consistency.
27
* Bug Fixes
38
* Added explicit testing against python 3.5.2 for Ubuntu 16.04, and 3.5.3 for Debian 9
49
* Added fallback definition of typing.Deque (taken from 3.5.4)

cmd2/argparse_completer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,8 @@ def _complete_for_arg(self, arg_action: argparse.Action,
606606
# No cases matched, raise an error
607607
raise CompletionError('Could not find CommandSet instance matching defining type for completer')
608608
args.append(cmd_set)
609-
args.append(self._cmd2_app)
609+
else:
610+
args.append(self._cmd2_app)
610611

611612
# Check if arg_choices.to_call expects arg_tokens
612613
to_call_params = inspect.signature(arg_choices.to_call).parameters

cmd2/argparse_custom.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,16 @@ def my_choices_function():
6868
argument. This is good in cases where the list of choices being generated
6969
relies on state data of the cmd2-based app.
7070
If bound to a cmd2.CommandSet subclass, it will pass the CommandSet instance
71-
as the `self` argument, and the app instance as the positional argument.
71+
as the `self` argument.
7272
73-
Example bound to cmd2.Cmd::
73+
Example::
7474
7575
def my_choices_method(self):
7676
...
7777
return my_generated_list
7878
7979
parser.add_argument("arg", choices_method=my_choices_method)
8080
81-
Example bound to cmd2.CommandSEt::
82-
83-
def my_choices_method(self, app: cmd2.Cmd):
84-
...
85-
return my_generated_list
86-
87-
parser.add_argument("arg", choices_method=my_choices_method)
8881
8982
``completer_function`` - pass a tab completion function that does custom
9083
completion. Since custom tab completion operations commonly need to modify

cmd2/cmd2.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from . import ansi, constants, plugin, utils
4646
from .argparse_custom import DEFAULT_ARGUMENT_PARSER, CompletionItem
4747
from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer
48-
from .command_definition import CommandSet, _partial_passthru
48+
from .command_definition import CommandSet
4949
from .constants import COMMAND_FUNC_PREFIX, COMPLETER_FUNC_PREFIX, HELP_FUNC_PREFIX
5050
from .decorators import with_argparser, as_subcommand_to
5151
from .exceptions import (
@@ -186,7 +186,7 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
186186
:param auto_load_commands: If True, cmd2 will check for all subclasses of `CommandSet`
187187
that are currently loaded by Python and automatically
188188
instantiate and register all commands. If False, CommandSets
189-
must be manually installed with `install_command_set`.
189+
must be manually installed with `register_command_set`.
190190
"""
191191
# If use_ipython is False, make sure the ipy command isn't available in this instance
192192
if not use_ipython:
@@ -264,7 +264,7 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
264264
self._cmd_to_command_sets = {} # type: Dict[str, CommandSet]
265265
if command_sets:
266266
for command_set in command_sets:
267-
self.install_command_set(command_set)
267+
self.register_command_set(command_set)
268268

269269
if auto_load_commands:
270270
self._autoload_commands()
@@ -452,11 +452,11 @@ def load_commandset_by_type(commandset_types: List[Type]) -> None:
452452
or len(init_sig.parameters) != 1
453453
or 'self' not in init_sig.parameters):
454454
cmdset = cmdset_type()
455-
self.install_command_set(cmdset)
455+
self.register_command_set(cmdset)
456456

457457
load_commandset_by_type(all_commandset_defs)
458458

459-
def install_command_set(self, cmdset: CommandSet) -> None:
459+
def register_command_set(self, cmdset: CommandSet) -> None:
460460
"""
461461
Installs a CommandSet, loading all commands defined in the CommandSet
462462
@@ -476,23 +476,20 @@ def install_command_set(self, cmdset: CommandSet) -> None:
476476
try:
477477
for method_name, method in methods:
478478
command = method_name[len(COMMAND_FUNC_PREFIX):]
479-
command_wrapper = _partial_passthru(method, self)
480479

481-
self._install_command_function(command, command_wrapper, type(cmdset).__name__)
480+
self._install_command_function(command, method, type(cmdset).__name__)
482481
installed_attributes.append(method_name)
483482

484483
completer_func_name = COMPLETER_FUNC_PREFIX + command
485484
cmd_completer = getattr(cmdset, completer_func_name, None)
486485
if cmd_completer is not None:
487-
completer_wrapper = _partial_passthru(cmd_completer, self)
488-
self._install_completer_function(command, completer_wrapper)
486+
self._install_completer_function(command, cmd_completer)
489487
installed_attributes.append(completer_func_name)
490488

491489
help_func_name = HELP_FUNC_PREFIX + command
492490
cmd_help = getattr(cmdset, help_func_name, None)
493491
if cmd_help is not None:
494-
help_wrapper = _partial_passthru(cmd_help, self)
495-
self._install_help_function(command, help_wrapper)
492+
self._install_help_function(command, cmd_help)
496493
installed_attributes.append(help_func_name)
497494

498495
self._cmd_to_command_sets[command] = cmdset
@@ -508,7 +505,7 @@ def install_command_set(self, cmdset: CommandSet) -> None:
508505
if cmdset in self._cmd_to_command_sets.values():
509506
self._cmd_to_command_sets = \
510507
{key: val for key, val in self._cmd_to_command_sets.items() if val is not cmdset}
511-
cmdset.on_unregister(self)
508+
cmdset.on_unregister()
512509
raise
513510

514511
def _install_command_function(self, command: str, command_wrapper: Callable, context=''):
@@ -549,7 +546,7 @@ def _install_help_function(self, cmd_name: str, cmd_help: Callable):
549546
raise CommandSetRegistrationError('Attribute already exists: {}'.format(help_func_name))
550547
setattr(self, help_func_name, cmd_help)
551548

552-
def uninstall_command_set(self, cmdset: CommandSet):
549+
def unregister_command_set(self, cmdset: CommandSet):
553550
"""
554551
Uninstalls a CommandSet and unloads all associated commands
555552
:param cmdset: CommandSet to uninstall
@@ -581,7 +578,7 @@ def uninstall_command_set(self, cmdset: CommandSet):
581578
if hasattr(self, HELP_FUNC_PREFIX + cmd_name):
582579
delattr(self, HELP_FUNC_PREFIX + cmd_name)
583580

584-
cmdset.on_unregister(self)
581+
cmdset.on_unregister()
585582
self._installed_command_sets.remove(cmdset)
586583

587584
def _check_uninstallable(self, cmdset: CommandSet):
@@ -656,11 +653,7 @@ def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
656653
raise CommandSetRegistrationError('Could not find argparser for command "{}" needed by subcommand: {}'
657654
.format(command_name, str(method)))
658655

659-
if isinstance(cmdset, CommandSet):
660-
command_handler = _partial_passthru(method, self)
661-
else:
662-
command_handler = method
663-
subcmd_parser.set_defaults(cmd2_handler=command_handler)
656+
subcmd_parser.set_defaults(cmd2_handler=method)
664657

665658
def find_subcommand(action: argparse.ArgumentParser, subcmd_names: List[str]) -> argparse.ArgumentParser:
666659
if not subcmd_names:

cmd2/command_definition.py

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,6 @@
1818
pass
1919

2020

21-
def _partial_passthru(func: Callable, *args, **kwargs) -> functools.partial:
22-
"""
23-
Constructs a partial function that passes arguments through to the wrapped function.
24-
Must construct a new type every time so that each wrapped function's __doc__ can be copied correctly.
25-
26-
:param func: wrapped function
27-
:param args: positional arguments
28-
:param kwargs: keyword arguments
29-
:return: partial function that exposes attributes of wrapped function
30-
"""
31-
def __getattr__(self, item):
32-
return getattr(self.func, item)
33-
34-
def __setattr__(self, key, value):
35-
return setattr(self.func, key, value)
36-
37-
def __dir__(self) -> Iterable[str]:
38-
return dir(self.func)
39-
40-
passthru_type = type('PassthruPartial' + func.__name__,
41-
(functools.partial,),
42-
{
43-
'__getattr__': __getattr__,
44-
'__setattr__': __setattr__,
45-
'__dir__': __dir__,
46-
})
47-
passthru_type.__doc__ = func.__doc__
48-
return passthru_type(func, *args, **kwargs)
49-
50-
5121
def with_default_category(category: str):
5222
"""
5323
Decorator that applies a category to all ``do_*`` command methods in a class that do not already
@@ -78,8 +48,7 @@ class CommandSet(object):
7848
7949
``with_default_category`` can be used to apply a default category to all commands in the CommandSet.
8050
81-
``do_``, ``help_``, and ``complete_`` functions differ only in that they're now required to accept
82-
a reference to ``cmd2.Cmd`` as the first argument after self.
51+
``do_``, ``help_``, and ``complete_`` functions differ only in that self is the CommandSet instead of the cmd2 app
8352
"""
8453

8554
def __init__(self):
@@ -98,7 +67,7 @@ def on_register(self, cmd):
9867
else:
9968
raise CommandSetRegistrationError('This CommandSet has already been registered')
10069

101-
def on_unregister(self, cmd):
70+
def on_unregister(self):
10271
"""
10372
Called by ``cmd2.Cmd`` when a CommandSet is unregistered and removed.
10473

cmd2/decorators.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,18 @@ def cat_decorator(func):
4141
##########################
4242

4343

44-
def _parse_positionals(args: Tuple) -> Tuple['cmd2.Cmd', Union[Statement, str]]:
44+
def _parse_positionals(args: Tuple) -> Tuple[Union['cmd2.Cmd', 'cmd2.CommandSet'], Union[Statement, str]]:
4545
"""
4646
Helper function for cmd2 decorators to inspect the positional arguments until the cmd2.Cmd argument is found
4747
Assumes that we will find cmd2.Cmd followed by the command statement object or string.
4848
:arg args: The positional arguments to inspect
4949
:return: The cmd2.Cmd reference and the command line statement
5050
"""
5151
for pos, arg in enumerate(args):
52-
from cmd2 import Cmd
53-
if isinstance(arg, Cmd) and len(args) > pos:
52+
from cmd2 import Cmd, CommandSet
53+
if (isinstance(arg, Cmd) or isinstance(arg, CommandSet)) and len(args) > pos:
54+
if isinstance(arg, CommandSet):
55+
arg = arg._cmd
5456
next_arg = args[pos + 1]
5557
if isinstance(next_arg, (Statement, str)):
5658
return arg, args[pos + 1]
@@ -92,7 +94,7 @@ def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) ->
9294
>>> def do_echo(self, arglist):
9395
>>> self.poutput(' '.join(arglist)
9496
"""
95-
import functools
97+
import functools, cmd2
9698

9799
def arg_decorator(func: Callable):
98100
@functools.wraps(func)
@@ -293,8 +295,8 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
293295
else:
294296
setattr(ns, '__statement__', statement)
295297

296-
def get_handler(self: argparse.Namespace) -> Optional[Callable]:
297-
return getattr(self, constants.SUBCMD_HANDLER, None)
298+
def get_handler(ns_self: argparse.Namespace) -> Optional[Callable]:
299+
return getattr(ns_self, constants.SUBCMD_HANDLER, None)
298300

299301
setattr(ns, 'get_handler', types.MethodType(get_handler, ns))
300302

0 commit comments

Comments
 (0)