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 adds
6 a new command in a CommandTree structure (kamaki.cli.commant_tree). The later
7 is used by interfaces to manage kamaki commands.
9 In the following, a set of kamaki commands will be implemented::
11 mygrp1 list all //show a list
12 mygrp1 list details [--match=<>] //show list of details
13 mygrp2 list all [regular expression] [-l] //list all subjects
14 mygrp2 info <id> [name] //get information for subject with id
16 There are two command sets to implement, namely mygrp1 and mygrp2. The first
17 will contain two commands, namely list-all and list-details. The second one
18 will also contain two commands, list-all and info. To avoid ambiguities,
19 command names should rather be prefixed with the group they belong to, e.g.
20 mygrp1-list-all and mygrp2-list-all.
22 The first command has the simplest possible syntax: no parameters, no runtime
23 arguments. The second accepts an optional runtime argument with a value. The
24 third features an optional argument and an optional runtime flag argument. The
25 last is an example of a command with an obligatory and an optional argument.
27 Samples of the expected behavior in one-command mode are following:
29 .. code-block:: console
43 details show a list of details
44 $ kamaki mygrp1 list all
45 ... (mygrp1 client method is called) ...
46 $ kamaki mygrp2 list all 'Z[.]' -l
47 ... (mygrp2 client method is called) ...
50 The above example will be used throughout the present guide.
52 The CommandTree structure
53 -------------------------
55 CommandTree manages a command by its path. Each command is stored in multiple
56 nodes on the tree, so that the last term is a leaf and the route from root to
57 that leaf represents the command path. For example the commands *file upload*,
58 *file list* and *file info* are stored together as shown bellow::
65 The example used in the present, should result to the creation of two trees::
77 Each command group should be stored on a different CommandTree. For that
78 reason, command specification modules should contain a list of CommandTree
79 objects, named *_commands*
81 A command group information (name, description) is provided at CommandTree
82 structure initialization:
84 .. code-block:: python
86 _mygrp1_commands = CommandTree('mygrp', 'mygrp1 description')
87 _mygrp2_commands = CommandTree('mygrp', 'mygrp2 description')
89 _commands = [_mygrp1_commands, _mygrp2_commands]
94 The *command* decorator mines all the information necessary to build a command
95 specification which is then inserted in a CommanTree instance::
97 class code ---> command() --> updated CommandTree structure
99 Kamaki interfaces make use of this CommandTree structure. Optimizations are
100 possible by using special parameters on the command decorator method.
102 .. code-block:: python
104 def command(cmd_tree, prefix='', descedants_depth=None):
105 """Load a class as a command
107 :param cmd_tree: is the CommandTree to be updated with a new command
109 :param prefix: of the commands allowed to be inserted ('' for all)
111 :param descedants_depth: is the depth of the tree descedants of the
115 Creating a new command specification set
116 ----------------------------------------
118 A command specification developer should create a new module (python file) with as many classes as the command specifications to be offered. Each class should be decorated with *command*.
120 .. code-block:: python
123 _commands = [_mygrp1_commands, _mygrp2_commands]
125 @command(_mygrp1_commands)
126 class mygrp1_list_all():
131 A list of CommandTree structures must exist in the module scope, with the name
132 _commands, as shown above. Different CommandTree objects correspond to
133 different command groups.
135 Get command description
136 -----------------------
138 The description of each command is the first line of the class commend. The
139 following declaration of *mygrp2-info* command has a "*get information for
140 subject with id*" description.
142 .. code-block:: python
145 @command(_mygrp2_commands)
147 """get information for subject with id"""
150 Declare run-time argument
151 -------------------------
153 The argument mechanism allows the definition of run-time arguments. Some basic
154 argument types are defined at the
155 `argument module <code.html#module-kamaki.cli.argument>`_, but it is not
156 uncommon to extent these classes in order to achieve specialized type checking
157 and syntax control (e.g. at
158 `pithos cli module <code.html#module-kamaki.cli.commands.pithos>`_).
160 To declare a run-time argument on a specific command, the object class should
161 initialize a dict called *arguments* , where Argument objects are stored. Each
162 argument object is a possible run-time argument. Syntax checking happens at
163 client level, while the type checking is implemented in the Argument code
164 (thus, many different Argument types might be needed).`
166 .. code-block:: python
168 from kamaki.cli.argument import ValueArgument
171 @command(_mygrp1_commands)
172 class mygrp1_list_details():
173 """list of details"""
175 def __init__(self, global_args={}):
176 global_args['match'] = ValueArgument(
177 'Filter results to match string',
179 self.arguments = global_args
181 or more usually and elegantly:
183 .. code-block:: python
185 from kamaki.cli.argument import ValueArgument
187 @command(_mygrp1_commands)
188 class mygrp1_list_details():
189 """List of details"""
193 'Filter output to match string', ('-m', --match'))
196 Accessing run-time arguments
197 ----------------------------
199 To access run-time arguments, users can use the _command_init interface, which
200 implements __item__ accessors to handle run-time argument values. In specific,
201 an instance of _command_init can use brackets to set or read <argument>.value .
203 .. code-block:: python
205 from kamaki.cli.argument import ValueArgument
206 from kamaki.cli.commands import _command_init
208 @command(_mygrp1_commands)
209 class mygrp1_list_details(_command_init):
210 """List of details"""
214 'Filter output to match string', ('-m', --match'))
217 def check_runtime_arguments(self):
219 assert self['match'] == self.arguments['match'].value
222 The main method and command parameters
223 --------------------------------------
225 The command behavior for each command / class is coded in *main*. The
226 parameters of *main* method defines the command parameters part of the syntax.
229 main(self, param) - obligatory parameter <param>
230 main(self, param=None) - optional parameter [param]
231 main(self, param1, param2=42) - <param1> [param2]
232 main(self, param1____param2) - <param1:param2>
233 main(self, param1____param2=[]) - [param1:param2]
234 main(self, param1____param2__) - <param1[:param2]>
235 main(self, param1____param2__='') - [param1[:param2]]
236 main(self, *args) - arbitary number of params [...]
237 main(self, param1____param2, *args) - <param1:param2> [...]
239 The information that can be mined by *command* for each individual command is
240 presented in the following:
242 .. code-block:: python
245 from kamaki.cli.argument import FlagArgument
248 _commands = [_mygrp1_commands, _mygrp2=commands]
251 @command(_mygrp2_commands)
252 class mygrp2_list_all():
253 """List all subjects"""
255 arguments = dict(FlagArgument('detailed list', '-l'))
257 def main(self, reg_exp=None):
260 This will load the following information on the CommandTree:
262 * Syntax (from lines 8,12,19): mygrp list all [reg exp] [-l]
263 * Description (form line 9): List all subjects
264 * Arguments help (from line 13,14): -l: detailed list
269 Kamaki will load a command specification *only* if it is set as a configurable
270 option. To demonstrate this, let the command specifications coded above be
271 stored in a file named *grps.py*.
273 The developer should move file *grps.py* to kamaki/cli/commands, the default
274 place for command specifications, although running a command specification from
275 a different path is also a kamaki feature.
277 The user has to use a configuration file where the following is added:
286 .. code-block:: console
288 $ kamaki config set mygrp1_cli grps
289 $ kamaki config set mygrp2_cli grps
291 Command specification modules don't need to live in kamaki/cli/commands,
292 although this is suggested for uniformity. If a command module exist in another
296 mygrp_cli = /another/path/grps.py
298 Summary: create a command set
299 -----------------------------
301 .. code-block:: python
305 from kamaki.cli.commands import _command_init
306 from kamaki.cli.command_tree import CommandTree
307 from kamaki.cli.argument import ValueArgument, FlagArgument
311 # Initiallize command trees
313 _mygrp1_commands = CommandTree('mygrp', 'mygrp1 description')
314 _mygrp2_commands = CommandTree('mygrp', 'mygrp2 description')
316 _commands = [_mygrp1_commands, _mygrp2_commands]
319 # Define command specifications
322 @command(_mygrp1_commands)
323 class mygrp1_list_all(_command_init):
330 @command(_mygrp1_commands)
331 class mygrp1_list_details(_command_init):
332 """show list of details"""
336 'Filter output to match string', ('-m', --match'))
341 match_value = self['match']
345 @command(_mygrp2_commands)
346 class mygrp2_list_all(_command_init):
347 """list all subjects"""
350 list=FlagArgument('detailed listing', '-l')
353 def main(self, regular_expression=None):
355 detail_flag = self['list']
360 if regular_expression:
365 @command(_mygrp2_commands)
366 class mygrp2_info(_command_init):
367 """get information for subject with id"""
369 def main(self, id, name=''):