Revision dc99e627
b/docs/developers/adding-commands.rst | ||
---|---|---|
1 | 1 |
Adding Commands |
2 | 2 |
=============== |
3 | 3 |
|
4 |
Kamaki commands are implemented as python classes, decorated with a special |
|
5 |
decorator called *command*. This decorator is a method of *kamaki.cli* that |
|
6 |
adds a new command in a *CommandTree* structure. A *CommandTree* (package |
|
7 |
*kamaki.cli.commant_tree*) is a data structure used by kamaki to manage command |
|
8 |
namespaces. |
|
4 |
Kamaki commands are implemented as python classes, which wear a decorator |
|
5 |
called *command*. The decorator lives in *kamaki.cli* and its purpose is to |
|
6 |
update the *CommandTree* structure. The *CommandTree* class ( |
|
7 |
*kamaki.cli.commant_tree*) manages command namespaces for kamaki. |
|
9 | 8 |
|
10 | 9 |
For demonstration purposes, the following set of kamaki commands will be |
11 | 10 |
implemented in this document:: |
12 | 11 |
|
13 |
mygrp1 list all //show a list |
|
14 |
mygrp1 list details [--match=<>] //show list of details |
|
15 |
mygrp2 list all [regular expression] [-l] //list all subjects
|
|
16 |
mygrp2 info <id> [name] //get information for subject with id
|
|
12 |
mygrp1 list all //show a list
|
|
13 |
mygrp1 list details [--match=<>] //show list of details
|
|
14 |
mygrp2 list all [regular expression] [-l] //list all subjects |
|
15 |
mygrp2 info <id> [--filter] //information on a subject
|
|
17 | 16 |
|
18 |
There are two command groups to implement i.e., *mygrp1* and *mygrp2*, |
|
17 |
.. note:: By convention, the names of the groups describe subjects e.g., |
|
18 |
"server", "network", "container", etc. |
|
19 |
|
|
20 |
Here we get two command groups to implement i.e., *mygrp1* and *mygrp2*, |
|
19 | 21 |
containing two commands each (*list_all*, *list_details* and *list_all*, *info* |
20 |
respectively). To avoid ambiguities, command names are prefixed with the |
|
21 |
command group they belong to, e.g., *mygrp1_list_all* and *mygrp2_list_all*. |
|
22 |
The underscore is used to separate command namespaces. |
|
22 |
respectively). The underscore is used to separate command namespaces and should |
|
23 |
be considered as a special character in this context. |
|
23 | 24 |
|
24 | 25 |
The first command (*mygrp1_list_all*) has the simplest possible syntax: no |
25 |
parameters, no runtime arguments. The second accepts an optional runtime argument with a value. The third features an optional parameter and an optional |
|
26 |
runtime flag argument. The last is an example of a command with an obligatory |
|
27 |
and an optional parameter. |
|
26 |
parameters, no runtime arguments. The second one defines one optional runtime |
|
27 |
argument with a value. The third features an optional parameter and an optional |
|
28 |
runtime flag argument. The last one is an example of a command with an |
|
29 |
obligatory and an optional parameter. |
|
28 | 30 |
|
29 |
Examples of the expected behavior in one-command mode:
|
|
31 |
Some examples:
|
|
30 | 32 |
|
31 | 33 |
.. code-block:: console |
32 | 34 |
|
... | ... | |
51 | 53 |
The CommandTree structure |
52 | 54 |
------------------------- |
53 | 55 |
|
54 |
CommandTree manages a command by its namespace. Each command is stored in |
|
55 |
a tree path, where each node is a name. A leaf is the end term of a namespace and contains a pointer to the command class to be executed. |
|
56 |
CommandTree manages commands and their namespaces. Each command is stored in |
|
57 |
a tree, where each node is a name. A leaf is the rightmost term of a namespace |
|
58 |
and contains a pointer to the executable command class. |
|
56 | 59 |
|
57 |
Here is an example from the actual kamaki command structure, where the commands
|
|
58 |
*file upload*, *file list* and *file info* are represented as shown bellow::
|
|
60 |
Here is an example from the actual kamaki command structure, featuring the
|
|
61 |
commands *file upload*, *file list* and *file info* ::
|
|
59 | 62 |
|
60 | 63 |
- file |
61 | 64 |
''''''''|- info |
... | ... | |
76 | 79 |
|
77 | 80 |
Each command group should be stored on a different CommandTree. |
78 | 81 |
|
79 |
For that reason, command specification modules should contain a list of CommandTree objects, named *_commands*. This mechanism allows any interface |
|
82 |
For that reason, command specification modules should contain a list of |
|
83 |
CommandTree objects, named *_commands*. This mechanism allows any interface |
|
80 | 84 |
application to load the list of commands from the *_commands* array. |
81 | 85 |
|
82 |
The first name of the command path and a description (name, description) are needed to initializeg a CommandTree: |
|
83 |
|
|
84 | 86 |
.. code-block:: python |
85 | 87 |
|
86 | 88 |
_mygrp1_commands = CommandTree('mygrp', 'mygrp1 description') |
... | ... | |
88 | 90 |
|
89 | 91 |
_commands = [_mygrp1_commands, _mygrp2_commands] |
90 | 92 |
|
93 |
.. note:: The name and the description, will later appear in automatically |
|
94 |
created help messages |
|
91 | 95 |
|
92 | 96 |
The command decorator |
93 | 97 |
--------------------- |
... | ... | |
95 | 99 |
All commands are specified by subclasses of *kamaki.cli.commands._command_init* |
96 | 100 |
These classes are called "command specifications". |
97 | 101 |
|
98 |
The *command* decorator mines all the information needed to build a namespace
|
|
102 |
The *command* decorator mines all the information needed to build namespaces
|
|
99 | 103 |
from a command specification:: |
100 | 104 |
|
101 | 105 |
class code ---> command() --> updated CommandTree structure |
... | ... | |
141 | 145 |
Set command description |
142 | 146 |
----------------------- |
143 | 147 |
|
144 |
The description of each command is the first line of the class commend. The |
|
145 |
following declaration of *mygrp2-info* command has a "*get information for |
|
146 |
subject with id*" description. |
|
148 |
The first line of the class commend is used as the command short description. |
|
149 |
The rest is used as the detailed description. |
|
147 | 150 |
|
148 | 151 |
.. code-block:: python |
149 | 152 |
|
... | ... | |
178 | 181 |
Declare run-time argument |
179 | 182 |
------------------------- |
180 | 183 |
|
181 |
A special argument mechanism allows the definition of run-time arguments. This |
|
182 |
mechanism is based on argparse and is designed to simplify argument definitions |
|
183 |
when specifying commands. |
|
184 |
The argument mechanism is based on the standard argparse module. |
|
184 | 185 |
|
185 | 186 |
Some basic argument types are defined at the |
186 | 187 |
`argument module <code.html#module-kamaki.cli.argument>`_, but it is not |
187 | 188 |
a bad idea to extent these classes in order to achieve specialized type |
188 |
checking and syntax control. Still, in most cases, the argument types of the |
|
189 |
argument package are enough for most cases. |
|
189 |
checking and syntax control with respect to the semantics of each command. |
|
190 |
Still, in most cases, the argument types of the argument package are enough for |
|
191 |
most cases. |
|
190 | 192 |
|
191 | 193 |
To declare a run-time argument on a specific command, the specification class |
192 | 194 |
should contain a dict called *arguments* , where Argument objects are stored. |
193 |
Each argument object is a run-time argument. Syntax checking happens at client
|
|
194 |
level, while the type checking is implemented in the Argument code (e.g.,
|
|
195 |
IntArgument checks if the value is an int).
|
|
195 |
Each argument object is a run-time argument. Syntax checking happens at the
|
|
196 |
command specification level, while the type checking is implemented in the
|
|
197 |
Argument subclasses.
|
|
196 | 198 |
|
197 | 199 |
.. code-block:: python |
198 | 200 |
|
... | ... | |
227 | 229 |
Accessing run-time arguments |
228 | 230 |
---------------------------- |
229 | 231 |
|
230 |
To access run-time arguments, users can use the *_command_init* interface, |
|
231 |
which implements *__item__* accessors to handle run-time argument values. In |
|
232 |
other words, one may get the value of an argument with *self[<argument>]*. |
|
232 |
To access run-time arguments, command classes extend the *_command_init* |
|
233 |
interface, which implements *__item__* accessors to handle run-time argument |
|
234 |
values. In other words, one may get the runtime value of an argument by calling |
|
235 |
*self[<argument>]*. |
|
233 | 236 |
|
234 | 237 |
.. code-block:: python |
235 | 238 |
|
... | ... | |
250 | 253 |
assert self['match'] == self.arguments['match'].value |
251 | 254 |
... |
252 | 255 |
|
256 |
Non-positional required arguments |
|
257 |
--------------------------------- |
|
258 |
|
|
259 |
By convention, kamaki uses positional arguments for identifiers and |
|
260 |
non-positional arguments for everything else. By default, non-positional |
|
261 |
arguments are optional. A non-positional argument can explicitly set to be |
|
262 |
required at command specification level: |
|
263 |
|
|
264 |
.. code-block:: python |
|
265 |
|
|
266 |
... |
|
267 |
|
|
268 |
@command(_mygrp1_commands) |
|
269 |
class mygrp1_list_details(_command_init): |
|
270 |
"""List of details""" |
|
271 |
|
|
272 |
arguments = dict( |
|
273 |
match=ValueArgument( |
|
274 |
'Filter output to match string', ('-m', --match')) |
|
275 |
) |
|
276 |
required = (match, ) |
|
277 |
|
|
278 |
A tupple means "all required", while a list notation means "at least one". |
|
279 |
|
|
280 |
|
|
253 | 281 |
The main method and command parameters |
254 | 282 |
-------------------------------------- |
255 | 283 |
|
... | ... | |
297 | 325 |
* Runtime arguments (line 13): [-l] |
298 | 326 |
* Runtime arguments help (line 13): detailed list |
299 | 327 |
|
300 |
.. tip:: It is suggested to code the main functionality in a member method
|
|
301 |
called *_run*. This allows the separation between syntax and logic. For
|
|
302 |
example, an external library may need to call a command without caring |
|
328 |
.. tip:: By convention, the main functionality is implemented in a member
|
|
329 |
method called *_run*. This allows the separation between syntax and logic.
|
|
330 |
For example, an external library may need to call a command without caring
|
|
303 | 331 |
about its command line behavior. |
304 | 332 |
|
305 | 333 |
Letting kamaki know |
306 | 334 |
------------------- |
307 | 335 |
|
308 |
Kamaki will load a command specification *only* if it is set as a configurable |
|
309 |
option. To demonstrate this, let the command specifications coded above be |
|
310 |
stored in a file named *grps.py*. |
|
336 |
Assume that the command specifications presented so far be stored in a file |
|
337 |
named *grps.py*. |
|
311 | 338 |
|
312 | 339 |
The developer should move the file *grps.py* to *kamaki/cli/commands*, the |
313 | 340 |
default place for command specifications |
Also available in: Unified diff