root / docs / developers / adding-commands.rst @ dc99e627
History | View | Annotate | Download (13.2 kB)
1 | 9e4508df | Stavros Sachtouris | Adding Commands |
---|---|---|---|
2 | 9e4508df | Stavros Sachtouris | =============== |
3 | 9e4508df | Stavros Sachtouris | |
4 | dc99e627 | Stavros Sachtouris | Kamaki commands are implemented as python classes, which wear a decorator |
5 | dc99e627 | Stavros Sachtouris | called *command*. The decorator lives in *kamaki.cli* and its purpose is to |
6 | dc99e627 | Stavros Sachtouris | update the *CommandTree* structure. The *CommandTree* class ( |
7 | dc99e627 | Stavros Sachtouris | *kamaki.cli.commant_tree*) manages command namespaces for kamaki. |
8 | 9e4508df | Stavros Sachtouris | |
9 | 16d7b9ff | Stavros Sachtouris | For demonstration purposes, the following set of kamaki commands will be |
10 | 16d7b9ff | Stavros Sachtouris | implemented in this document:: |
11 | 9e4508df | Stavros Sachtouris | |
12 | dc99e627 | Stavros Sachtouris | mygrp1 list all //show a list |
13 | dc99e627 | Stavros Sachtouris | mygrp1 list details [--match=<>] //show list of details |
14 | dc99e627 | Stavros Sachtouris | mygrp2 list all [regular expression] [-l] //list all subjects |
15 | dc99e627 | Stavros Sachtouris | mygrp2 info <id> [--filter] //information on a subject |
16 | 9e4508df | Stavros Sachtouris | |
17 | dc99e627 | Stavros Sachtouris | .. note:: By convention, the names of the groups describe subjects e.g., |
18 | dc99e627 | Stavros Sachtouris | "server", "network", "container", etc. |
19 | dc99e627 | Stavros Sachtouris | |
20 | dc99e627 | Stavros Sachtouris | Here we get two command groups to implement i.e., *mygrp1* and *mygrp2*, |
21 | 16d7b9ff | Stavros Sachtouris | containing two commands each (*list_all*, *list_details* and *list_all*, *info* |
22 | dc99e627 | Stavros Sachtouris | respectively). The underscore is used to separate command namespaces and should |
23 | dc99e627 | Stavros Sachtouris | be considered as a special character in this context. |
24 | 9e4508df | Stavros Sachtouris | |
25 | 16d7b9ff | Stavros Sachtouris | The first command (*mygrp1_list_all*) has the simplest possible syntax: no |
26 | dc99e627 | Stavros Sachtouris | parameters, no runtime arguments. The second one defines one optional runtime |
27 | dc99e627 | Stavros Sachtouris | argument with a value. The third features an optional parameter and an optional |
28 | dc99e627 | Stavros Sachtouris | runtime flag argument. The last one is an example of a command with an |
29 | dc99e627 | Stavros Sachtouris | obligatory and an optional parameter. |
30 | 9e4508df | Stavros Sachtouris | |
31 | dc99e627 | Stavros Sachtouris | Some examples: |
32 | 9e4508df | Stavros Sachtouris | |
33 | 9e4508df | Stavros Sachtouris | .. code-block:: console |
34 | 9e4508df | Stavros Sachtouris | |
35 | 9e4508df | Stavros Sachtouris | $kamaki mygrp1 |
36 | 9e4508df | Stavros Sachtouris | mygrp1 description |
37 | 9e4508df | Stavros Sachtouris | |
38 | 9e4508df | Stavros Sachtouris | Options |
39 | 9e4508df | Stavros Sachtouris | - - - - |
40 | 9e4508df | Stavros Sachtouris | list |
41 | 9e4508df | Stavros Sachtouris | $ kamaki mygrp1 list |
42 | 9e4508df | Stavros Sachtouris | |
43 | 9e4508df | Stavros Sachtouris | Options |
44 | 9e4508df | Stavros Sachtouris | - - - - |
45 | 9e4508df | Stavros Sachtouris | all show a list |
46 | 9e4508df | Stavros Sachtouris | details show a list of details |
47 | 9e4508df | Stavros Sachtouris | $ kamaki mygrp1 list all |
48 | 16d7b9ff | Stavros Sachtouris | ... (a mygrp1_list_all instance runs) ... |
49 | 9e4508df | Stavros Sachtouris | $ kamaki mygrp2 list all 'Z[.]' -l |
50 | 16d7b9ff | Stavros Sachtouris | ... (a mygrp2_list_all instance runs) ... |
51 | 9e4508df | Stavros Sachtouris | $ |
52 | 9e4508df | Stavros Sachtouris | |
53 | 9e4508df | Stavros Sachtouris | The CommandTree structure |
54 | 9e4508df | Stavros Sachtouris | ------------------------- |
55 | 9e4508df | Stavros Sachtouris | |
56 | dc99e627 | Stavros Sachtouris | CommandTree manages commands and their namespaces. Each command is stored in |
57 | dc99e627 | Stavros Sachtouris | a tree, where each node is a name. A leaf is the rightmost term of a namespace |
58 | dc99e627 | Stavros Sachtouris | and contains a pointer to the executable command class. |
59 | 16d7b9ff | Stavros Sachtouris | |
60 | dc99e627 | Stavros Sachtouris | Here is an example from the actual kamaki command structure, featuring the |
61 | dc99e627 | Stavros Sachtouris | commands *file upload*, *file list* and *file info* :: |
62 | 9e4508df | Stavros Sachtouris | |
63 | 0ea31480 | Stavros Sachtouris | - file |
64 | 9e4508df | Stavros Sachtouris | ''''''''|- info |
65 | 9e4508df | Stavros Sachtouris | |- list |
66 | 9e4508df | Stavros Sachtouris | |- upload |
67 | 9e4508df | Stavros Sachtouris | |
68 | 16d7b9ff | Stavros Sachtouris | Now, let's load the showcase example on CommandTrees:: |
69 | 9e4508df | Stavros Sachtouris | |
70 | 9e4508df | Stavros Sachtouris | - mygrp1 |
71 | 9e4508df | Stavros Sachtouris | ''''''''|- list |
72 | 9e4508df | Stavros Sachtouris | '''''''|- all |
73 | 9e4508df | Stavros Sachtouris | |- details |
74 | 9e4508df | Stavros Sachtouris | |
75 | 9e4508df | Stavros Sachtouris | - mygrp2 |
76 | 9e4508df | Stavros Sachtouris | ''''''''|- list |
77 | 9e4508df | Stavros Sachtouris | '''''''|- all |
78 | 9e4508df | Stavros Sachtouris | |- info |
79 | 9e4508df | Stavros Sachtouris | |
80 | 16d7b9ff | Stavros Sachtouris | Each command group should be stored on a different CommandTree. |
81 | 16d7b9ff | Stavros Sachtouris | |
82 | dc99e627 | Stavros Sachtouris | For that reason, command specification modules should contain a list of |
83 | dc99e627 | Stavros Sachtouris | CommandTree objects, named *_commands*. This mechanism allows any interface |
84 | 16d7b9ff | Stavros Sachtouris | application to load the list of commands from the *_commands* array. |
85 | 9e4508df | Stavros Sachtouris | |
86 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
87 | 9e4508df | Stavros Sachtouris | |
88 | 9e4508df | Stavros Sachtouris | _mygrp1_commands = CommandTree('mygrp', 'mygrp1 description') |
89 | 9e4508df | Stavros Sachtouris | _mygrp2_commands = CommandTree('mygrp', 'mygrp2 description') |
90 | 9e4508df | Stavros Sachtouris | |
91 | 9e4508df | Stavros Sachtouris | _commands = [_mygrp1_commands, _mygrp2_commands] |
92 | 9e4508df | Stavros Sachtouris | |
93 | dc99e627 | Stavros Sachtouris | .. note:: The name and the description, will later appear in automatically |
94 | dc99e627 | Stavros Sachtouris | created help messages |
95 | 16d7b9ff | Stavros Sachtouris | |
96 | 9e4508df | Stavros Sachtouris | The command decorator |
97 | 9e4508df | Stavros Sachtouris | --------------------- |
98 | 9e4508df | Stavros Sachtouris | |
99 | 16d7b9ff | Stavros Sachtouris | All commands are specified by subclasses of *kamaki.cli.commands._command_init* |
100 | 16d7b9ff | Stavros Sachtouris | These classes are called "command specifications". |
101 | 16d7b9ff | Stavros Sachtouris | |
102 | dc99e627 | Stavros Sachtouris | The *command* decorator mines all the information needed to build namespaces |
103 | 16d7b9ff | Stavros Sachtouris | from a command specification:: |
104 | 9e4508df | Stavros Sachtouris | |
105 | 9e4508df | Stavros Sachtouris | class code ---> command() --> updated CommandTree structure |
106 | 9e4508df | Stavros Sachtouris | |
107 | 16d7b9ff | Stavros Sachtouris | Kamaki interfaces make use of the CommandTree structure. Optimizations are |
108 | fa382f9e | Stavros Sachtouris | possible by using special parameters on the command decorator method. |
109 | 9e4508df | Stavros Sachtouris | |
110 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
111 | 9e4508df | Stavros Sachtouris | |
112 | 9e4508df | Stavros Sachtouris | def command(cmd_tree, prefix='', descedants_depth=None): |
113 | 9e4508df | Stavros Sachtouris | """Load a class as a command |
114 | fa382f9e | Stavros Sachtouris | |
115 | 59cadffb | Stavros Sachtouris | :param cmd_tree: is the CommandTree to be updated with a new command |
116 | fa382f9e | Stavros Sachtouris | |
117 | 59cadffb | Stavros Sachtouris | :param prefix: of the commands allowed to be inserted ('' for all) |
118 | fa382f9e | Stavros Sachtouris | |
119 | 16d7b9ff | Stavros Sachtouris | :param descedants_depth: is the depth of the tree descendants of the |
120 | 9e4508df | Stavros Sachtouris | prefix command. |
121 | 9e4508df | Stavros Sachtouris | """ |
122 | 9e4508df | Stavros Sachtouris | |
123 | 9e4508df | Stavros Sachtouris | Creating a new command specification set |
124 | 9e4508df | Stavros Sachtouris | ---------------------------------------- |
125 | 9e4508df | Stavros Sachtouris | |
126 | 16d7b9ff | Stavros Sachtouris | A command specification developer should create a new module (python file) with |
127 | 16d7b9ff | Stavros Sachtouris | one command specification class per command. Each class should be decorated |
128 | 16d7b9ff | Stavros Sachtouris | with *command*. |
129 | 9e4508df | Stavros Sachtouris | |
130 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
131 | 9e4508df | Stavros Sachtouris | |
132 | 9e4508df | Stavros Sachtouris | ... |
133 | 9e4508df | Stavros Sachtouris | _commands = [_mygrp1_commands, _mygrp2_commands] |
134 | 9e4508df | Stavros Sachtouris | |
135 | 9e4508df | Stavros Sachtouris | @command(_mygrp1_commands) |
136 | 9e4508df | Stavros Sachtouris | class mygrp1_list_all(): |
137 | 9e4508df | Stavros Sachtouris | ... |
138 | 9e4508df | Stavros Sachtouris | |
139 | 9e4508df | Stavros Sachtouris | ... |
140 | 9e4508df | Stavros Sachtouris | |
141 | fa382f9e | Stavros Sachtouris | A list of CommandTree structures must exist in the module scope, with the name |
142 | 16d7b9ff | Stavros Sachtouris | *_commands*. Different CommandTree objects correspond to different command |
143 | 16d7b9ff | Stavros Sachtouris | groups. |
144 | 9e4508df | Stavros Sachtouris | |
145 | 16d7b9ff | Stavros Sachtouris | Set command description |
146 | 9e4508df | Stavros Sachtouris | ----------------------- |
147 | 9e4508df | Stavros Sachtouris | |
148 | dc99e627 | Stavros Sachtouris | The first line of the class commend is used as the command short description. |
149 | dc99e627 | Stavros Sachtouris | The rest is used as the detailed description. |
150 | 9e4508df | Stavros Sachtouris | |
151 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
152 | 9e4508df | Stavros Sachtouris | |
153 | 9e4508df | Stavros Sachtouris | ... |
154 | 9e4508df | Stavros Sachtouris | @command(_mygrp2_commands) |
155 | 16d7b9ff | Stavros Sachtouris | class mygrp2_info(): |
156 | 16d7b9ff | Stavros Sachtouris | """get information for subject with id |
157 | 16d7b9ff | Stavros Sachtouris | Anything from this point and bellow constitutes the long description |
158 | 16d7b9ff | Stavros Sachtouris | Please, mind the indentation, pep8 is not forgiving. |
159 | 16d7b9ff | Stavros Sachtouris | """ |
160 | 9e4508df | Stavros Sachtouris | ... |
161 | 9e4508df | Stavros Sachtouris | |
162 | 16d7b9ff | Stavros Sachtouris | Description placeholders |
163 | 16d7b9ff | Stavros Sachtouris | ------------------------ |
164 | 16d7b9ff | Stavros Sachtouris | |
165 | 16d7b9ff | Stavros Sachtouris | There is possible to create an empty command, that can act as a description |
166 | 16d7b9ff | Stavros Sachtouris | placeholder. For example, the *mygrp1_list* namespace does not correspond to an |
167 | 16d7b9ff | Stavros Sachtouris | executable command, but it can have a helpful description. In that case, create |
168 | 16d7b9ff | Stavros Sachtouris | a command specification class with a command and no code: |
169 | 16d7b9ff | Stavros Sachtouris | |
170 | 16d7b9ff | Stavros Sachtouris | .. code-block:: python |
171 | 16d7b9ff | Stavros Sachtouris | |
172 | 16d7b9ff | Stavros Sachtouris | @command(_mygrp1_commands) |
173 | 16d7b9ff | Stavros Sachtouris | class mygrp1_list(): |
174 | 16d7b9ff | Stavros Sachtouris | """List mygrp1 objects. |
175 | 16d7b9ff | Stavros Sachtouris | There are two versions: short and detailed |
176 | 16d7b9ff | Stavros Sachtouris | """ |
177 | 16d7b9ff | Stavros Sachtouris | |
178 | 16d7b9ff | Stavros Sachtouris | .. warning:: A command specification class with no description is invalid and |
179 | 16d7b9ff | Stavros Sachtouris | will cause an error. |
180 | 16d7b9ff | Stavros Sachtouris | |
181 | 9e4508df | Stavros Sachtouris | Declare run-time argument |
182 | 9e4508df | Stavros Sachtouris | ------------------------- |
183 | 9e4508df | Stavros Sachtouris | |
184 | dc99e627 | Stavros Sachtouris | The argument mechanism is based on the standard argparse module. |
185 | 16d7b9ff | Stavros Sachtouris | |
186 | 16d7b9ff | Stavros Sachtouris | Some basic argument types are defined at the |
187 | fa382f9e | Stavros Sachtouris | `argument module <code.html#module-kamaki.cli.argument>`_, but it is not |
188 | 16d7b9ff | Stavros Sachtouris | a bad idea to extent these classes in order to achieve specialized type |
189 | dc99e627 | Stavros Sachtouris | checking and syntax control with respect to the semantics of each command. |
190 | dc99e627 | Stavros Sachtouris | Still, in most cases, the argument types of the argument package are enough for |
191 | dc99e627 | Stavros Sachtouris | most cases. |
192 | 9e4508df | Stavros Sachtouris | |
193 | 16d7b9ff | Stavros Sachtouris | To declare a run-time argument on a specific command, the specification class |
194 | 16d7b9ff | Stavros Sachtouris | should contain a dict called *arguments* , where Argument objects are stored. |
195 | dc99e627 | Stavros Sachtouris | Each argument object is a run-time argument. Syntax checking happens at the |
196 | dc99e627 | Stavros Sachtouris | command specification level, while the type checking is implemented in the |
197 | dc99e627 | Stavros Sachtouris | Argument subclasses. |
198 | 9e4508df | Stavros Sachtouris | |
199 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
200 | 9e4508df | Stavros Sachtouris | |
201 | 9e4508df | Stavros Sachtouris | from kamaki.cli.argument import ValueArgument |
202 | 9e4508df | Stavros Sachtouris | ... |
203 | 9e4508df | Stavros Sachtouris | |
204 | 9e4508df | Stavros Sachtouris | @command(_mygrp1_commands) |
205 | 9e4508df | Stavros Sachtouris | class mygrp1_list_details(): |
206 | 9e4508df | Stavros Sachtouris | """list of details""" |
207 | 9e4508df | Stavros Sachtouris | |
208 | 59cadffb | Stavros Sachtouris | def __init__(self, global_args={}): |
209 | 9e4508df | Stavros Sachtouris | global_args['match'] = ValueArgument( |
210 | 9e4508df | Stavros Sachtouris | 'Filter results to match string', |
211 | fa382f9e | Stavros Sachtouris | ('-m', '--match')) |
212 | 9e4508df | Stavros Sachtouris | self.arguments = global_args |
213 | 9e4508df | Stavros Sachtouris | |
214 | fa382f9e | Stavros Sachtouris | or more usually and elegantly: |
215 | fa382f9e | Stavros Sachtouris | |
216 | fa382f9e | Stavros Sachtouris | .. code-block:: python |
217 | fa382f9e | Stavros Sachtouris | |
218 | fa382f9e | Stavros Sachtouris | from kamaki.cli.argument import ValueArgument |
219 | fa382f9e | Stavros Sachtouris | |
220 | fa382f9e | Stavros Sachtouris | @command(_mygrp1_commands) |
221 | fa382f9e | Stavros Sachtouris | class mygrp1_list_details(): |
222 | fa382f9e | Stavros Sachtouris | """List of details""" |
223 | fa382f9e | Stavros Sachtouris | |
224 | fa382f9e | Stavros Sachtouris | arguments = dict( |
225 | fa382f9e | Stavros Sachtouris | match=ValueArgument( |
226 | 16d7b9ff | Stavros Sachtouris | 'Filter output to match string', ('-m', --match')) |
227 | fa382f9e | Stavros Sachtouris | ) |
228 | fa382f9e | Stavros Sachtouris | |
229 | fa382f9e | Stavros Sachtouris | Accessing run-time arguments |
230 | fa382f9e | Stavros Sachtouris | ---------------------------- |
231 | fa382f9e | Stavros Sachtouris | |
232 | dc99e627 | Stavros Sachtouris | To access run-time arguments, command classes extend the *_command_init* |
233 | dc99e627 | Stavros Sachtouris | interface, which implements *__item__* accessors to handle run-time argument |
234 | dc99e627 | Stavros Sachtouris | values. In other words, one may get the runtime value of an argument by calling |
235 | dc99e627 | Stavros Sachtouris | *self[<argument>]*. |
236 | fa382f9e | Stavros Sachtouris | |
237 | fa382f9e | Stavros Sachtouris | .. code-block:: python |
238 | fa382f9e | Stavros Sachtouris | |
239 | fa382f9e | Stavros Sachtouris | from kamaki.cli.argument import ValueArgument |
240 | fa382f9e | Stavros Sachtouris | from kamaki.cli.commands import _command_init |
241 | fa382f9e | Stavros Sachtouris | |
242 | fa382f9e | Stavros Sachtouris | @command(_mygrp1_commands) |
243 | fa382f9e | Stavros Sachtouris | class mygrp1_list_details(_command_init): |
244 | fa382f9e | Stavros Sachtouris | """List of details""" |
245 | fa382f9e | Stavros Sachtouris | |
246 | fa382f9e | Stavros Sachtouris | arguments = dict( |
247 | fa382f9e | Stavros Sachtouris | match=ValueArgument( |
248 | fa382f9e | Stavros Sachtouris | 'Filter output to match string', ('-m', --match')) |
249 | fa382f9e | Stavros Sachtouris | ) |
250 | fa382f9e | Stavros Sachtouris | |
251 | fa382f9e | Stavros Sachtouris | def check_runtime_arguments(self): |
252 | fa382f9e | Stavros Sachtouris | ... |
253 | fa382f9e | Stavros Sachtouris | assert self['match'] == self.arguments['match'].value |
254 | fa382f9e | Stavros Sachtouris | ... |
255 | fa382f9e | Stavros Sachtouris | |
256 | dc99e627 | Stavros Sachtouris | Non-positional required arguments |
257 | dc99e627 | Stavros Sachtouris | --------------------------------- |
258 | dc99e627 | Stavros Sachtouris | |
259 | dc99e627 | Stavros Sachtouris | By convention, kamaki uses positional arguments for identifiers and |
260 | dc99e627 | Stavros Sachtouris | non-positional arguments for everything else. By default, non-positional |
261 | dc99e627 | Stavros Sachtouris | arguments are optional. A non-positional argument can explicitly set to be |
262 | dc99e627 | Stavros Sachtouris | required at command specification level: |
263 | dc99e627 | Stavros Sachtouris | |
264 | dc99e627 | Stavros Sachtouris | .. code-block:: python |
265 | dc99e627 | Stavros Sachtouris | |
266 | dc99e627 | Stavros Sachtouris | ... |
267 | dc99e627 | Stavros Sachtouris | |
268 | dc99e627 | Stavros Sachtouris | @command(_mygrp1_commands) |
269 | dc99e627 | Stavros Sachtouris | class mygrp1_list_details(_command_init): |
270 | dc99e627 | Stavros Sachtouris | """List of details""" |
271 | dc99e627 | Stavros Sachtouris | |
272 | dc99e627 | Stavros Sachtouris | arguments = dict( |
273 | dc99e627 | Stavros Sachtouris | match=ValueArgument( |
274 | dc99e627 | Stavros Sachtouris | 'Filter output to match string', ('-m', --match')) |
275 | dc99e627 | Stavros Sachtouris | ) |
276 | dc99e627 | Stavros Sachtouris | required = (match, ) |
277 | dc99e627 | Stavros Sachtouris | |
278 | dc99e627 | Stavros Sachtouris | A tupple means "all required", while a list notation means "at least one". |
279 | dc99e627 | Stavros Sachtouris | |
280 | dc99e627 | Stavros Sachtouris | |
281 | 9e4508df | Stavros Sachtouris | The main method and command parameters |
282 | 9e4508df | Stavros Sachtouris | -------------------------------------- |
283 | 9e4508df | Stavros Sachtouris | |
284 | 16d7b9ff | Stavros Sachtouris | The command behavior for each command class is coded in *main*. The |
285 | 16d7b9ff | Stavros Sachtouris | parameters of *main* method affect the syntax of the command. In specific:: |
286 | 9e4508df | Stavros Sachtouris | |
287 | 59cadffb | Stavros Sachtouris | main(self, param) - obligatory parameter <param> |
288 | 59cadffb | Stavros Sachtouris | main(self, param=None) - optional parameter [param] |
289 | 9e4508df | Stavros Sachtouris | main(self, param1, param2=42) - <param1> [param2] |
290 | 9e4508df | Stavros Sachtouris | main(self, param1____param2) - <param1:param2> |
291 | 9e4508df | Stavros Sachtouris | main(self, param1____param2=[]) - [param1:param2] |
292 | 9e4508df | Stavros Sachtouris | main(self, param1____param2__) - <param1[:param2]> |
293 | 9e4508df | Stavros Sachtouris | main(self, param1____param2__='') - [param1[:param2]] |
294 | 9e4508df | Stavros Sachtouris | main(self, *args) - arbitary number of params [...] |
295 | 9e4508df | Stavros Sachtouris | main(self, param1____param2, *args) - <param1:param2> [...] |
296 | 9e4508df | Stavros Sachtouris | |
297 | 16d7b9ff | Stavros Sachtouris | Let's have a look at the command specification class again, and highlight the |
298 | 16d7b9ff | Stavros Sachtouris | parts that affect the command syntax: |
299 | 9e4508df | Stavros Sachtouris | |
300 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
301 | 9e4508df | Stavros Sachtouris | :linenos: |
302 | 9e4508df | Stavros Sachtouris | |
303 | 9e4508df | Stavros Sachtouris | from kamaki.cli.argument import FlagArgument |
304 | 9e4508df | Stavros Sachtouris | ... |
305 | 9e4508df | Stavros Sachtouris | |
306 | 16d7b9ff | Stavros Sachtouris | _commands = [_mygrp1_commands, _mygrp2_commands] |
307 | 9e4508df | Stavros Sachtouris | ... |
308 | 9e4508df | Stavros Sachtouris | |
309 | 9e4508df | Stavros Sachtouris | @command(_mygrp2_commands) |
310 | fa382f9e | Stavros Sachtouris | class mygrp2_list_all(): |
311 | 16d7b9ff | Stavros Sachtouris | """List all subjects |
312 | 16d7b9ff | Stavros Sachtouris | Refers to the subject accessible by current user |
313 | 16d7b9ff | Stavros Sachtouris | """ |
314 | 9e4508df | Stavros Sachtouris | |
315 | fa382f9e | Stavros Sachtouris | arguments = dict(FlagArgument('detailed list', '-l')) |
316 | 9e4508df | Stavros Sachtouris | |
317 | 9e4508df | Stavros Sachtouris | def main(self, reg_exp=None): |
318 | 9e4508df | Stavros Sachtouris | ... |
319 | 9e4508df | Stavros Sachtouris | |
320 | 16d7b9ff | Stavros Sachtouris | The above lines contain the following information: |
321 | 16d7b9ff | Stavros Sachtouris | |
322 | 16d7b9ff | Stavros Sachtouris | * Namespace and name (line 8): mygrp2 list all |
323 | 16d7b9ff | Stavros Sachtouris | * Short (line 9) and long (line 10) description |
324 | 16d7b9ff | Stavros Sachtouris | * Parameters (line 15): [reg exp] |
325 | 16d7b9ff | Stavros Sachtouris | * Runtime arguments (line 13): [-l] |
326 | 16d7b9ff | Stavros Sachtouris | * Runtime arguments help (line 13): detailed list |
327 | 9e4508df | Stavros Sachtouris | |
328 | dc99e627 | Stavros Sachtouris | .. tip:: By convention, the main functionality is implemented in a member |
329 | dc99e627 | Stavros Sachtouris | method called *_run*. This allows the separation between syntax and logic. |
330 | dc99e627 | Stavros Sachtouris | For example, an external library may need to call a command without caring |
331 | 16d7b9ff | Stavros Sachtouris | about its command line behavior. |
332 | 9e4508df | Stavros Sachtouris | |
333 | 9e4508df | Stavros Sachtouris | Letting kamaki know |
334 | 9e4508df | Stavros Sachtouris | ------------------- |
335 | 9e4508df | Stavros Sachtouris | |
336 | dc99e627 | Stavros Sachtouris | Assume that the command specifications presented so far be stored in a file |
337 | dc99e627 | Stavros Sachtouris | named *grps.py*. |
338 | 9e4508df | Stavros Sachtouris | |
339 | 16d7b9ff | Stavros Sachtouris | The developer should move the file *grps.py* to *kamaki/cli/commands*, the |
340 | 16d7b9ff | Stavros Sachtouris | default place for command specifications |
341 | 9e4508df | Stavros Sachtouris | |
342 | 16d7b9ff | Stavros Sachtouris | These lines should be contained in the kamaki configuration file for a new |
343 | 16d7b9ff | Stavros Sachtouris | command specification module to work: |
344 | 9e4508df | Stavros Sachtouris | :: |
345 | 9e4508df | Stavros Sachtouris | |
346 | fa382f9e | Stavros Sachtouris | [global] |
347 | fa382f9e | Stavros Sachtouris | mygrp1_cli = grps |
348 | fa382f9e | Stavros Sachtouris | mygrp2_cli = grps |
349 | 9e4508df | Stavros Sachtouris | |
350 | fa382f9e | Stavros Sachtouris | or equivalently: |
351 | 9e4508df | Stavros Sachtouris | |
352 | 9e4508df | Stavros Sachtouris | .. code-block:: console |
353 | 9e4508df | Stavros Sachtouris | |
354 | fa382f9e | Stavros Sachtouris | $ kamaki config set mygrp1_cli grps |
355 | fa382f9e | Stavros Sachtouris | $ kamaki config set mygrp2_cli grps |
356 | 9e4508df | Stavros Sachtouris | |
357 | 16d7b9ff | Stavros Sachtouris | .. note:: running a command specification from a different path is supported. |
358 | 16d7b9ff | Stavros Sachtouris | To achieve this, add a *<group>_cli = </path/to/module>* line in the |
359 | e372fc24 | Stavros Sachtouris | configure file under the *global* section |
360 | e372fc24 | Stavros Sachtouris | |
361 | e372fc24 | Stavros Sachtouris | An example:: |
362 | 9e4508df | Stavros Sachtouris | |
363 | fa382f9e | Stavros Sachtouris | [global] |
364 | fa382f9e | Stavros Sachtouris | mygrp_cli = /another/path/grps.py |
365 | 9e4508df | Stavros Sachtouris | |
366 | 9e4508df | Stavros Sachtouris | Summary: create a command set |
367 | 9e4508df | Stavros Sachtouris | ----------------------------- |
368 | 9e4508df | Stavros Sachtouris | |
369 | 9e4508df | Stavros Sachtouris | .. code-block:: python |
370 | 9e4508df | Stavros Sachtouris | |
371 | 9e4508df | Stavros Sachtouris | # File: grps.py |
372 | 9e4508df | Stavros Sachtouris | |
373 | fa382f9e | Stavros Sachtouris | from kamaki.cli.commands import _command_init |
374 | 9e4508df | Stavros Sachtouris | from kamaki.cli.command_tree import CommandTree |
375 | 9e4508df | Stavros Sachtouris | from kamaki.cli.argument import ValueArgument, FlagArgument |
376 | 9e4508df | Stavros Sachtouris | ... |
377 | 9e4508df | Stavros Sachtouris | |
378 | 9e4508df | Stavros Sachtouris | |
379 | 9e4508df | Stavros Sachtouris | # Initiallize command trees |
380 | 9e4508df | Stavros Sachtouris | |
381 | 9e4508df | Stavros Sachtouris | _mygrp1_commands = CommandTree('mygrp', 'mygrp1 description') |
382 | 9e4508df | Stavros Sachtouris | _mygrp2_commands = CommandTree('mygrp', 'mygrp2 description') |
383 | 9e4508df | Stavros Sachtouris | |
384 | 9e4508df | Stavros Sachtouris | _commands = [_mygrp1_commands, _mygrp2_commands] |
385 | 9e4508df | Stavros Sachtouris | |
386 | 9e4508df | Stavros Sachtouris | |
387 | 9e4508df | Stavros Sachtouris | # Define command specifications |
388 | 9e4508df | Stavros Sachtouris | |
389 | 9e4508df | Stavros Sachtouris | |
390 | 9e4508df | Stavros Sachtouris | @command(_mygrp1_commands) |
391 | 16d7b9ff | Stavros Sachtouris | class mygrp1_list(_command_init): |
392 | 16d7b9ff | Stavros Sachtouris | """List mygrp1 objects. |
393 | 16d7b9ff | Stavros Sachtouris | There are two versions: short and detailed |
394 | 16d7b9ff | Stavros Sachtouris | """ |
395 | 16d7b9ff | Stavros Sachtouris | |
396 | 16d7b9ff | Stavros Sachtouris | |
397 | 16d7b9ff | Stavros Sachtouris | @command(_mygrp1_commands) |
398 | fa382f9e | Stavros Sachtouris | class mygrp1_list_all(_command_init): |
399 | 9e4508df | Stavros Sachtouris | """show a list""" |
400 | 9e4508df | Stavros Sachtouris | |
401 | 16d7b9ff | Stavros Sachtouris | def _run(): |
402 | 9e4508df | Stavros Sachtouris | ... |
403 | 9e4508df | Stavros Sachtouris | |
404 | 16d7b9ff | Stavros Sachtouris | def main(self): |
405 | 16d7b9ff | Stavros Sachtouris | self._run() |
406 | 16d7b9ff | Stavros Sachtouris | |
407 | 9e4508df | Stavros Sachtouris | |
408 | 9e4508df | Stavros Sachtouris | @command(_mygrp1_commands) |
409 | fa382f9e | Stavros Sachtouris | class mygrp1_list_details(_command_init): |
410 | 9e4508df | Stavros Sachtouris | """show list of details""" |
411 | 9e4508df | Stavros Sachtouris | |
412 | fa382f9e | Stavros Sachtouris | arguments = dict( |
413 | fa382f9e | Stavros Sachtouris | match=ValueArgument( |
414 | fa382f9e | Stavros Sachtouris | 'Filter output to match string', ('-m', --match')) |
415 | fa382f9e | Stavros Sachtouris | ) |
416 | 9e4508df | Stavros Sachtouris | |
417 | 16d7b9ff | Stavros Sachtouris | def _run(self): |
418 | fa382f9e | Stavros Sachtouris | match_value = self['match'] |
419 | 9e4508df | Stavros Sachtouris | ... |
420 | 9e4508df | Stavros Sachtouris | |
421 | 16d7b9ff | Stavros Sachtouris | def main(self): |
422 | 16d7b9ff | Stavros Sachtouris | self._run() |
423 | 16d7b9ff | Stavros Sachtouris | |
424 | 16d7b9ff | Stavros Sachtouris | |
425 | 16d7b9ff | Stavros Sachtouris | #The following will also create a mygrp2_list command with no description |
426 | 16d7b9ff | Stavros Sachtouris | |
427 | 9e4508df | Stavros Sachtouris | |
428 | 9e4508df | Stavros Sachtouris | @command(_mygrp2_commands) |
429 | fa382f9e | Stavros Sachtouris | class mygrp2_list_all(_command_init): |
430 | 9e4508df | Stavros Sachtouris | """list all subjects""" |
431 | 9e4508df | Stavros Sachtouris | |
432 | fa382f9e | Stavros Sachtouris | arguments = dict( |
433 | fa382f9e | Stavros Sachtouris | list=FlagArgument('detailed listing', '-l') |
434 | fa382f9e | Stavros Sachtouris | ) |
435 | 9e4508df | Stavros Sachtouris | |
436 | 16d7b9ff | Stavros Sachtouris | def _run(self, regexp): |
437 | 9e4508df | Stavros Sachtouris | ... |
438 | 16d7b9ff | Stavros Sachtouris | if self['list']: |
439 | 9e4508df | Stavros Sachtouris | ... |
440 | 16d7b9ff | Stavros Sachtouris | else: |
441 | 9e4508df | Stavros Sachtouris | ... |
442 | 16d7b9ff | Stavros Sachtouris | |
443 | 16d7b9ff | Stavros Sachtouris | def main(self, regular_expression=None): |
444 | 16d7b9ff | Stavros Sachtouris | self._run(regular_expression) |
445 | 9e4508df | Stavros Sachtouris | |
446 | 9e4508df | Stavros Sachtouris | |
447 | 9e4508df | Stavros Sachtouris | @command(_mygrp2_commands) |
448 | fa382f9e | Stavros Sachtouris | class mygrp2_info(_command_init): |
449 | 9e4508df | Stavros Sachtouris | """get information for subject with id""" |
450 | 9e4508df | Stavros Sachtouris | |
451 | 16d7b9ff | Stavros Sachtouris | def _run(self, grp_id, grp_name): |
452 | 9e4508df | Stavros Sachtouris | ... |
453 | 16d7b9ff | Stavros Sachtouris | |
454 | 16d7b9ff | Stavros Sachtouris | def main(self, id, name=''): |
455 | 16d7b9ff | Stavros Sachtouris | self._run(id, name) |