Skip to content

Commit 28a4891

Browse files
committed
Simplified creation of cmd2-specific argparse.Action attributes.
1 parent b1e07db commit 28a4891

5 files changed

Lines changed: 132 additions & 384 deletions

File tree

cmd2/argparse_completer.py

Lines changed: 58 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,10 @@
2222
cast,
2323
)
2424

25-
from rich.text import Text
26-
27-
from .constants import INFINITY
28-
from .rich_utils import Cmd2SimpleTable
29-
30-
if TYPE_CHECKING: # pragma: no cover
31-
from .cmd2 import Cmd
32-
3325
from rich.table import Column
26+
from rich.text import Text
3427

3528
from .argparse_custom import (
36-
ChoicesCallable,
3729
Cmd2ArgumentParser,
3830
generate_range_error,
3931
)
@@ -43,7 +35,18 @@
4335
Completions,
4436
all_display_numeric,
4537
)
38+
from .constants import INFINITY
4639
from .exceptions import CompletionError
40+
from .rich_utils import Cmd2SimpleTable
41+
from .types import (
42+
ChoicesProviderUnbound,
43+
CmdOrSet,
44+
CompleterUnbound,
45+
)
46+
47+
if TYPE_CHECKING: # pragma: no cover
48+
from .cmd2 import Cmd
49+
4750

4851
# Name of the choice/completer function argument that, if present, will be passed a dictionary of
4952
# command line tokens up through the token being completed mapped to their argparse destination name.
@@ -708,33 +711,32 @@ def print_help(self, tokens: Sequence[str], file: IO[str] | None = None) -> None
708711
return
709712
self._parser.print_help(file=file)
710713

711-
def _get_raw_choices(self, arg_state: _ArgumentState) -> list[CompletionItem] | ChoicesCallable | None:
712-
"""Extract choices from action or return the choices_callable."""
713-
if arg_state.action.choices is not None:
714-
# If choices are subcommands, then get their help text to populate display_meta.
715-
if isinstance(arg_state.action, argparse._SubParsersAction):
716-
parser_help = {}
717-
for action in arg_state.action._choices_actions:
718-
if action.dest in arg_state.action.choices:
719-
subparser = arg_state.action.choices[action.dest]
720-
parser_help[subparser] = action.help or ''
721-
722-
return [
723-
CompletionItem(name, display_meta=parser_help.get(subparser, ''))
724-
for name, subparser in arg_state.action.choices.items()
725-
]
726-
727-
# Standard choices
714+
def _choices_to_items(self, arg_state: _ArgumentState) -> list[CompletionItem]:
715+
"""Convert choices from action to list of CompletionItems."""
716+
if arg_state.action.choices is None:
717+
return []
718+
719+
# If choices are subcommands, then get their help text to populate display_meta.
720+
if isinstance(arg_state.action, argparse._SubParsersAction):
721+
parser_help = {}
722+
for action in arg_state.action._choices_actions:
723+
if action.dest in arg_state.action.choices:
724+
subparser = arg_state.action.choices[action.dest]
725+
parser_help[subparser] = action.help or ''
726+
728727
return [
729-
choice if isinstance(choice, CompletionItem) else CompletionItem(choice) for choice in arg_state.action.choices
728+
CompletionItem(name, display_meta=parser_help.get(subparser, ''))
729+
for name, subparser in arg_state.action.choices.items()
730730
]
731731

732-
choices_callable: ChoicesCallable | None = arg_state.action.get_choices_callable() # type: ignore[attr-defined]
733-
return choices_callable
732+
# Standard choices
733+
return [
734+
choice if isinstance(choice, CompletionItem) else CompletionItem(choice) for choice in arg_state.action.choices
735+
]
734736

735737
def _prepare_callable_params(
736738
self,
737-
choices_callable: ChoicesCallable,
739+
to_call: ChoicesProviderUnbound[CmdOrSet] | CompleterUnbound[CmdOrSet],
738740
arg_state: _ArgumentState,
739741
text: str,
740742
consumed_arg_values: dict[str, list[str]],
@@ -745,14 +747,14 @@ def _prepare_callable_params(
745747
kwargs: dict[str, Any] = {}
746748

747749
# Resolve the 'self' instance for the method
748-
self_arg = self._cmd2_app._resolve_func_self(choices_callable.to_call, cmd_set)
750+
self_arg = self._cmd2_app._resolve_func_self(to_call, cmd_set)
749751
if self_arg is None:
750-
raise CompletionError("Could not find CommandSet instance matching defining type for completer")
752+
raise CompletionError("Could not find CommandSet instance matching defining type")
751753

752754
args.append(self_arg)
753755

754756
# Check if the function expects 'arg_tokens'
755-
to_call_params = inspect.signature(choices_callable.to_call).parameters
757+
to_call_params = inspect.signature(to_call).parameters
756758
if ARG_TOKENS in to_call_params:
757759
arg_tokens = {**self._parent_tokens, **consumed_arg_values}
758760
arg_tokens.setdefault(arg_state.action.dest, []).append(text)
@@ -776,26 +778,33 @@ def _complete_arg(
776778
:return: a Completions object
777779
:raises CompletionError: if the completer or choices function this calls raises one
778780
"""
779-
raw_choices = self._get_raw_choices(arg_state)
780-
if not raw_choices:
781-
return Completions()
782-
783-
# Check if the argument uses a completer function
784-
if isinstance(raw_choices, ChoicesCallable) and raw_choices.is_completer:
785-
args, kwargs = self._prepare_callable_params(raw_choices, arg_state, text, consumed_arg_values, cmd_set)
781+
# Check if the argument uses a completer
782+
completer = arg_state.action.get_completer() # type: ignore[attr-defined]
783+
if completer is not None:
784+
args, kwargs = self._prepare_callable_params(
785+
completer,
786+
arg_state,
787+
text,
788+
consumed_arg_values,
789+
cmd_set,
790+
)
786791
args.extend([text, line, begidx, endidx])
787-
completions = raw_choices.completer(*args, **kwargs)
792+
completions: Completions = completer(*args, **kwargs)
788793

789-
# Otherwise it uses a choices list or choices provider function
794+
# Otherwise it uses a choices provider or choices list
790795
else:
791-
all_choices: list[CompletionItem] = []
792-
793-
if isinstance(raw_choices, ChoicesCallable):
794-
args, kwargs = self._prepare_callable_params(raw_choices, arg_state, text, consumed_arg_values, cmd_set)
795-
choices_func = raw_choices.choices_provider
796-
all_choices = list(choices_func(*args, **kwargs))
796+
choices_provider = arg_state.action.get_choices_provider() # type: ignore[attr-defined]
797+
if choices_provider is not None:
798+
args, kwargs = self._prepare_callable_params(
799+
choices_provider,
800+
arg_state,
801+
text,
802+
consumed_arg_values,
803+
cmd_set,
804+
)
805+
all_choices = list(choices_provider(*args, **kwargs))
797806
else:
798-
all_choices = raw_choices
807+
all_choices = self._choices_to_items(arg_state)
799808

800809
# Filter used values and run basic completion
801810
used_values = consumed_arg_values.get(arg_state.action.dest, [])

0 commit comments

Comments
 (0)