1
1
#!/usr/bin/env python3
2
- """A simple example demonstrating modular subcommand loading through CommandSets .
2
+ """This example revolves around the CommandSet feature for modularizing commands .
3
3
4
- In this example, there are loadable CommandSets defined. Each CommandSet has 1 subcommand defined that will be
5
- attached to the 'cut' command.
4
+ It attempts to cover basic usage as well as more complex usage including dynamic loading and unloading of CommandSets, using
5
+ CommandSets to add subcommands, as well as how to categorize command in CommandSets. Here we have kept the implementation for
6
+ most commands trivial because the intent is to focus on the CommandSet feature set.
6
7
7
- The cut command is implemented with the `do_cut` function that has been tagged as an argparse command.
8
+ The `AutoLoadCommandSet` is a basic command set which is loaded automatically at application startup and stays loaded until
9
+ application exit. Ths is the simplest case of simply modularizing command definitions to different classes and/or files.
8
10
9
- The `load` and `unload` command will load and unload the CommandSets. The available top level commands as well as
10
- subcommands to the `cut` command will change depending on which CommandSets are loaded.
11
+ The `LoadableFruits` and `LoadableVegetables` CommandSets are dynamically loadable and un-loadable at runtime using the `load`
12
+ and `unload` commands. This demonstrates the ability to load and unload CommandSets based on application state. Each of these
13
+ also loads a subcommand of the `cut` command.
11
14
"""
12
15
13
16
import argparse
20
23
with_default_category ,
21
24
)
22
25
26
+ COMMANDSET_BASIC = "Basic CommandSet"
27
+ COMMANDSET_DYNAMIC = "Dynamic CommandSet"
28
+ COMMANDSET_LOAD_UNLOAD = "Loading and Unloading CommandSets"
29
+ COMMANDSET_SUBCOMMAND = "Subcommands with CommandSet"
23
30
24
- @with_default_category ('Fruits' )
31
+
32
+ @with_default_category (COMMANDSET_BASIC )
33
+ class AutoLoadCommandSet (CommandSet ):
34
+ def __init__ (self ) -> None :
35
+ super ().__init__ ()
36
+
37
+ def do_hello (self , _ : cmd2 .Statement ) -> None :
38
+ """Prints hello."""
39
+ self ._cmd .poutput ('Hello' )
40
+
41
+ def do_world (self , _ : cmd2 .Statement ) -> None :
42
+ """Prints World."""
43
+ self ._cmd .poutput ('World' )
44
+
45
+
46
+ @with_default_category (COMMANDSET_DYNAMIC )
25
47
class LoadableFruits (CommandSet ):
26
48
def __init__ (self ) -> None :
27
49
super ().__init__ ()
28
50
29
51
def do_apple (self , _ : cmd2 .Statement ) -> None :
52
+ """Prints Apple."""
30
53
self ._cmd .poutput ('Apple' )
31
54
55
+ def do_banana (self , _ : cmd2 .Statement ) -> None :
56
+ """Prints Banana"""
57
+ self ._cmd .poutput ('Banana' )
58
+
32
59
banana_description = "Cut a banana"
33
60
banana_parser = cmd2 .Cmd2ArgumentParser (description = banana_description )
34
61
banana_parser .add_argument ('direction' , choices = ['discs' , 'lengthwise' ])
@@ -39,38 +66,48 @@ def cut_banana(self, ns: argparse.Namespace) -> None:
39
66
self ._cmd .poutput ('cutting banana: ' + ns .direction )
40
67
41
68
42
- @with_default_category ('Vegetables' )
69
+ @with_default_category (COMMANDSET_DYNAMIC )
43
70
class LoadableVegetables (CommandSet ):
44
71
def __init__ (self ) -> None :
45
72
super ().__init__ ()
46
73
47
74
def do_arugula (self , _ : cmd2 .Statement ) -> None :
75
+ "Prints Arguula."
48
76
self ._cmd .poutput ('Arugula' )
49
77
78
+ def do_bokchoy (self , _ : cmd2 .Statement ) -> None :
79
+ """Prints Bok Choy."""
80
+ self ._cmd .poutput ('Bok Choy' )
81
+
50
82
bokchoy_description = "Cut some bokchoy"
51
83
bokchoy_parser = cmd2 .Cmd2ArgumentParser (description = bokchoy_description )
52
84
bokchoy_parser .add_argument ('style' , choices = ['quartered' , 'diced' ])
53
85
54
86
@cmd2 .as_subcommand_to ('cut' , 'bokchoy' , bokchoy_parser , help = bokchoy_description .lower ())
55
- def cut_bokchoy (self , _ : argparse .Namespace ) -> None :
56
- self ._cmd .poutput ('Bok Choy' )
87
+ def cut_bokchoy (self , ns : argparse .Namespace ) -> None :
88
+ self ._cmd .poutput ('Bok Choy: ' + ns . style )
57
89
58
90
59
- class ExampleApp (cmd2 .Cmd ):
91
+ class CommandSetApp (cmd2 .Cmd ):
60
92
"""CommandSets are automatically loaded. Nothing needs to be done."""
61
93
62
- def __init__ (self , * args , ** kwargs ) -> None :
63
- # gotta have this or neither the plugin or cmd2 will initialize
64
- super ().__init__ (* args , auto_load_commands = False , ** kwargs )
94
+ def __init__ (self ) -> None :
95
+ # This prevents all CommandSets from auto-loading, which is necessary if you don't want some to load at startup
96
+ super ().__init__ (auto_load_commands = False )
97
+
98
+ self .register_command_set (AutoLoadCommandSet ())
65
99
100
+ # Store the dyanmic CommandSet classes for ease of loading and unloading
66
101
self ._fruits = LoadableFruits ()
67
102
self ._vegetables = LoadableVegetables ()
68
103
104
+ self .intro = 'The CommandSet feature allows defining commands in multiple files and the dynamic load/unload at runtime'
105
+
69
106
load_parser = cmd2 .Cmd2ArgumentParser ()
70
107
load_parser .add_argument ('cmds' , choices = ['fruits' , 'vegetables' ])
71
108
72
109
@with_argparser (load_parser )
73
- @with_category ('Command Loading' )
110
+ @with_category (COMMANDSET_LOAD_UNLOAD )
74
111
def do_load (self , ns : argparse .Namespace ) -> None :
75
112
if ns .cmds == 'fruits' :
76
113
try :
@@ -87,6 +124,7 @@ def do_load(self, ns: argparse.Namespace) -> None:
87
124
self .poutput ('Vegetables already loaded' )
88
125
89
126
@with_argparser (load_parser )
127
+ @with_category (COMMANDSET_LOAD_UNLOAD )
90
128
def do_unload (self , ns : argparse .Namespace ) -> None :
91
129
if ns .cmds == 'fruits' :
92
130
self .unregister_command_set (self ._fruits )
@@ -100,8 +138,9 @@ def do_unload(self, ns: argparse.Namespace) -> None:
100
138
cut_subparsers = cut_parser .add_subparsers (title = 'item' , help = 'item to cut' )
101
139
102
140
@with_argparser (cut_parser )
141
+ @with_category (COMMANDSET_SUBCOMMAND )
103
142
def do_cut (self , ns : argparse .Namespace ) -> None :
104
- # Call handler for whatever subcommand was selected
143
+ """Intended to be used with dyanmically loaded subcommands specifically."""
105
144
handler = ns .cmd2_handler .get ()
106
145
if handler is not None :
107
146
handler (ns )
@@ -112,5 +151,5 @@ def do_cut(self, ns: argparse.Namespace) -> None:
112
151
113
152
114
153
if __name__ == '__main__' :
115
- app = ExampleApp ()
154
+ app = CommandSetApp ()
116
155
app .cmdloop ()
0 commit comments