From 6da2cf30311f97d23a7121f8c02f9123674194b4 Mon Sep 17 00:00:00 2001 From: Eric Lin Date: Fri, 12 Jun 2020 20:44:10 -0400 Subject: Some minor cleanup of how imports work. Fixed issue with help documentation for CommandSet commands. Issue #943 --- cmd2/__init__.py | 1 + cmd2/cmd2.py | 8 ++++---- cmd2/command_definition.py | 21 ++++++++++++++++++--- 3 files changed, 23 insertions(+), 7 deletions(-) (limited to 'cmd2') diff --git a/cmd2/__init__.py b/cmd2/__init__.py index c3c1f87e..70a52f70 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -28,6 +28,7 @@ if cmd2_parser_module is not None: # Get the current value for argparse_custom.DEFAULT_ARGUMENT_PARSER from .argparse_custom import DEFAULT_ARGUMENT_PARSER from .cmd2 import Cmd +from .command_definition import CommandSet, with_default_category, register_command from .constants import COMMAND_NAME, DEFAULT_SHORTCUTS from .decorators import with_argument_list, with_argparser, with_argparser_and_unknown_args, with_category from .exceptions import Cmd2ArgparseError, SkipPostcommandHooks diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 9a98b550..4100ec08 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -46,7 +46,7 @@ from typing import Any, AnyStr, Callable, Dict, Iterable, List, Mapping, Optiona from . import ansi, constants, plugin, utils from .argparse_custom import DEFAULT_ARGUMENT_PARSER, CompletionItem from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer -from .command_definition import _UNBOUND_COMMANDS, CommandSet, _PartialPassthru +from .command_definition import _UNBOUND_COMMANDS, CommandSet, _partial_passthru from .constants import COMMAND_FUNC_PREFIX, COMPLETER_FUNC_PREFIX, HELP_FUNC_PREFIX from .decorators import with_argparser from .exceptions import Cmd2ShlexError, EmbeddedConsoleExit, EmptyStatement, RedirectionError, SkipPostcommandHooks @@ -428,7 +428,7 @@ class Cmd(cmd.Cmd): assert getattr(self, method[0], None) is None, \ 'In {}: Duplicate command function: {}'.format(cmdset_type.__name__, method[0]) - command_wrapper = _PartialPassthru(method[1], self) + command_wrapper = _partial_passthru(method[1], self) setattr(self, method[0], command_wrapper) command = method[0][len(COMMAND_FUNC_PREFIX):] @@ -436,11 +436,11 @@ class Cmd(cmd.Cmd): completer_func_name = COMPLETER_FUNC_PREFIX + command cmd_completer = getattr(cmdset, completer_func_name, None) if cmd_completer and not getattr(self, completer_func_name, None): - completer_wrapper = _PartialPassthru(cmd_completer, self) + completer_wrapper = _partial_passthru(cmd_completer, self) setattr(self, completer_func_name, completer_wrapper) cmd_help = getattr(cmdset, HELP_FUNC_PREFIX + command, None) if cmd_help and not getattr(self, HELP_FUNC_PREFIX + command, None): - help_wrapper = _PartialPassthru(cmd_help, self) + help_wrapper = _partial_passthru(cmd_help, self) setattr(self, HELP_FUNC_PREFIX + command, help_wrapper) def add_settable(self, settable: Settable) -> None: diff --git a/cmd2/command_definition.py b/cmd2/command_definition.py index f08040bb..a235525d 100644 --- a/cmd2/command_definition.py +++ b/cmd2/command_definition.py @@ -29,10 +29,15 @@ Registered command tuples. (command, do_ function, complete_ function, help_ fun """ -class _PartialPassthru(functools.partial): +def _partial_passthru(func: Callable, *args, **kwargs) -> functools.partial: """ - Wrapper around partial function that passes through getattr, setattr, and dir to the wrapped function. - This allows for CommandSet functions to be wrapped while maintaining the decorated properties + Constructs a partial function that passes arguments through to the wrapped function. + Must construct a new type every time so that each wrapped function's __doc__ can be copied correctly. + + :param func: wrapped function + :param args: positional arguments + :param kwargs: keyword arguments + :return: partial function that exposes attributes of wrapped function """ def __getattr__(self, item): return getattr(self.func, item) @@ -43,6 +48,16 @@ class _PartialPassthru(functools.partial): def __dir__(self) -> Iterable[str]: return dir(self.func) + passthru_type = type('PassthruPartial' + func.__name__, + (functools.partial,), + { + '__getattr__': __getattr__, + '__setattr__': __setattr__, + '__dir__': __dir__, + }) + passthru_type.__doc__ = func.__doc__ + return passthru_type(func, *args, **kwargs) + def register_command(cmd_func: Callable[['Cmd', Union['Statement', 'argparse.Namespace']], None]): """ -- cgit v1.2.1