summaryrefslogtreecommitdiff
path: root/examples/modular_commands_main.py
blob: 348374813d914b81536dcffb77d21f0109de4962 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python
# coding=utf-8
"""
A complex example demonstrating a variety of methods to load CommandSets using a mix of command decorators
with examples of how to integrate tab completion with argparse-based commands.
"""
import argparse
from typing import Dict, Iterable, List, Optional

from modular_commands.commandset_basic import BasicCompletionCommandSet  # noqa: F401
from modular_commands.commandset_complex import CommandSetA  # noqa: F401
from modular_commands.commandset_custominit import CustomInitCommandSet  # noqa: F401

from cmd2 import Cmd, Cmd2ArgumentParser, CommandSet, CompletionItem, with_argparser
from cmd2.utils import CompletionError, basic_complete

# Data source for argparse.choices
food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato']


def choices_function() -> List[str]:
    """Choices functions are useful when the choice list is dynamically generated (e.g. from data in a database)"""
    return ['a', 'dynamic', 'list', 'goes', 'here']


def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]:
    """
    A tab completion function not dependent on instance data. Since custom tab completion operations commonly
    need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer
    function. completer_method should be used in those cases.
    """
    match_against = ['a', 'dynamic', 'list', 'goes', 'here']
    return basic_complete(text, line, begidx, endidx, match_against)


def choices_completion_item() -> List[CompletionItem]:
    """Return CompletionItem instead of strings. These give more context to what's being tab completed."""
    items = {1: "My item", 2: "Another item", 3: "Yet another item"}
    return [CompletionItem(item_id, description) for item_id, description in items.items()]


def choices_arg_tokens(arg_tokens: Dict[str, List[str]]) -> List[str]:
    """
    If a choices or completer function/method takes a value called arg_tokens, then it will be
    passed a dictionary that maps the command line tokens up through the one being completed
    to their argparse argument name.  All values of the arg_tokens dictionary are lists, even if
    a particular argument expects only 1 token.
    """
    # Check if choices_function flag has appeared
    values = ['choices_function', 'flag']
    if 'choices_function' in arg_tokens:
        values.append('is {}'.format(arg_tokens['choices_function'][0]))
    else:
        values.append('not supplied')
    return values


class WithCommandSets(Cmd):
    def __init__(self, command_sets: Optional[Iterable[CommandSet]] = None):
        super().__init__(command_sets=command_sets)
        self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball']

    def choices_method(self) -> List[str]:
        """Choices methods are useful when the choice list is based on instance data of your application"""
        return self.sport_item_strs

    def choices_completion_error(self) -> List[str]:
        """
        CompletionErrors can be raised if an error occurs while tab completing.

        Example use cases
            - Reading a database to retrieve a tab completion data set failed
            - A previous command line argument that determines the data set being completed is invalid
        """
        if self.debug:
            return self.sport_item_strs
        raise CompletionError("debug must be true")

    # Parser for example command
    example_parser = Cmd2ArgumentParser(
        description="Command demonstrating tab completion with argparse\n" "Notice even the flags of this command tab complete"
    )

    # Tab complete from a list using argparse choices. Set metavar if you don't
    # want the entire choices list showing in the usage text for this command.
    example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", help="tab complete using choices")

    # Tab complete from choices provided by a choices function and choices method
    example_parser.add_argument(
        '--choices_function', choices_function=choices_function, help="tab complete using a choices_function"
    )
    example_parser.add_argument('--choices_method', choices_method=choices_method, help="tab complete using a choices_method")

    # Tab complete using a completer function and completer method
    example_parser.add_argument(
        '--completer_function', completer_function=completer_function, help="tab complete using a completer_function"
    )
    example_parser.add_argument(
        '--completer_method', completer_method=Cmd.path_complete, help="tab complete using a completer_method"
    )

    # Demonstrate raising a CompletionError while tab completing
    example_parser.add_argument(
        '--completion_error',
        choices_method=choices_completion_error,
        help="raise a CompletionError while tab completing if debug is False",
    )

    # Demonstrate returning CompletionItems instead of strings
    example_parser.add_argument(
        '--completion_item',
        choices_function=choices_completion_item,
        metavar="ITEM_ID",
        descriptive_header="Description",
        help="demonstrate use of CompletionItems",
    )

    # Demonstrate use of arg_tokens dictionary
    example_parser.add_argument(
        '--arg_tokens', choices_function=choices_arg_tokens, help="demonstrate use of arg_tokens dictionary"
    )

    @with_argparser(example_parser)
    def do_example(self, _: argparse.Namespace) -> None:
        """The example command"""
        self.poutput("I do nothing")


if __name__ == '__main__':
    import sys

    print("Starting")
    my_sets = [CustomInitCommandSet('First argument', 'Second argument')]
    app = WithCommandSets(command_sets=my_sets)
    sys.exit(app.cmdloop())