Statistics
| Branch: | Tag: | Revision:

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)