Rename command (mixed with method "command")
[kamaki] / kamaki / cli / command_tree.py
1 # Copyright 2011 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #         copyright notice, this list of conditions and the following
9 #         disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #         copyright notice, this list of conditions and the following
13 #         disclaimer in the documentation and/or other materials
14 #         provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 import cmd
35 #from .errors import CLIUnknownCommand, CLICmdIncompleteError, CLICmdSpecError, CLIError
36
37 class Command(object):
38         """Store a command and the next-level commands as well - no deep tree here"""
39         _name = None
40         path = None
41         cmd_class = None
42         subcommands = {}
43         help = ' '
44
45         def __init__(self, path, help = ' ', subcommands={}, cmd_class=None):
46                 self.path = path
47                 self.help = help
48                 self.subcommands =dict(subcommands)
49                 self.cmd_class = cmd_class
50
51         @property 
52         def name(self):
53                 if self._name is None:
54                         self._name = self.path.split('_')[-1]
55                 return str(self._name)
56
57         def add_subcmd(self, subcmd):
58                 if subcmd.path == self.path+'_'+subcmd.name:
59                         self.subcommands[subcmd.name] = subcmd
60                         return True
61                 return False
62         def get_subcmd(self, name):
63                 try:
64                         return self.subcommands[name]
65                 except KeyError:
66                         return None
67
68         def contains(self, name):
69                 """Check if a name is a direct child of self"""
70                 return self.subcommands.has_key(name)
71
72         @property 
73         def is_command(self):
74                 return self.cmd_class is not None
75         @property 
76         def has_description(self):
77                 return len(self.help.strip()) > 0
78         @property 
79         def description(self):
80                 return self.help
81
82         def set_class(self, cmd_class):
83                 self.cmd_class = cmd_class
84         def get_class(self):
85                 return self.cmd_class
86
87         def get_subnames(self):
88                 return self.subcommands.keys()
89         def get_subcommands(self):
90                 return self.subcommands.values()
91         def sublen(self):
92                 return len(self.subcommands)
93
94         def pretty_print(self, recursive=False):
95                 print('Path: %s (Name: %s) is_cmd: %s\n\thelp: %s'%(self.path, self.name,
96                         self.is_command, self.help))
97                 for cmd in self.get_subcommands():
98                         cmd.pretty_print(recursive)
99
100 def test_Command():
101         cmd = Command('store', 'A store thingy')
102         cmd.add_subcmd(Command('store_list'))
103         tmp = cmd.get_subcmd('list')
104         tmp.add_subcmd(Command('store_list_all', 'List everything'))
105         tmp.add_subcmd(Command('store_list_one', 'List just one stuff'))
106         cmd.pretty_print(True)
107
108 class CommandTree(object):
109
110         groups = {}
111         _all_commands = {}
112         name = None
113         description = None
114
115         def __init__(self, name, description=''):
116                 self.name = name
117                 self.description = description
118
119         def add_command(self, command_path, description=None, cmd_class=None):
120                 terms = command_path.split('_')
121                 try:
122                         cmd = self.groups[terms[0]]
123                 except KeyError:
124                         cmd = Command(terms[0])
125                         self.groups[terms[0]] = cmd
126                         self._all_commands[terms[0]] = cmd
127                 path = terms[0]
128                 for term in terms[1:]:
129                         path += '_'+term
130                         try:
131                                 cmd = cmd.subcommands[term]
132                         except KeyError:
133                                 new_cmd = Command(path)
134                                 self._all_commands[path] = new_cmd
135                                 cmd.add_subcmd(new_cmd)
136                                 cmd = new_cmd
137                 if cmd_class is not None:
138                         cmd.set_class(cmd_class)
139                 if description is not None:
140                         cmd.help = description
141         def get_command(self, path):
142                 return self._all_commands[path]
143         def get_groups(self):
144                 return self.groups.values()
145         def get_group_names(self):
146                 return self.groups.keys()
147
148         def set_description(self, path, description):
149                 self._all_commands[path].help = description
150         def get_descitpion(self, path):
151                 return self._all_commands[path].help
152         def set_class(self, path, cmd_class):
153                 self._all_commands[path].set_class(cmd_class)
154         def get_class(self, path):
155                 return self._all_commands[path].get_class()
156
157         def get_subnames(self, path):
158                 return self._all_commands[path].get_subnames()
159         def get_subcommands(self, path):
160                 return self._all_commands[path].get_subcommands()
161         def get_parent(self, path):
162                 if '_' not in path:
163                         return None
164                 terms = path.split('_')
165                 parent_path = '_'.join(terms[:-1])
166                 return self._all_commands[parent_path]
167         def get_closest_ancestor_command(self, path):
168                 path, sep, name = path.rpartition('_')
169                 while len(path) > 0:
170                         cmd = self._all_commands[path]
171                         if cmd.is_command:
172                                 return cmd
173                         path, sep, name = path.rpartition('_')
174                 return None
175
176                 if '_' not in path:
177                         return None
178                 terms = terms[:-1]
179                 while len(terms) > 0:
180                         tmp_path = '_'.join(terms)
181                         cmd = self._all_commands[tmp_path]
182                         if cmd.is_command:
183                                 return cmd
184                         terms = terms[:-1]
185                 raise KeyError('No ancestor commands')
186
187         def pretty_print(self, group=None):
188                 if group is None:
189                         for group in self.groups:
190                                 self.pretty_print(group)
191                 else:
192                         self.groups[group].pretty_print(recursive=True)
193
194 def test_CommandTree():
195         tree = CommandTree('kamaki', 'the kamaki tools')
196         tree.add_command('store', 'A storage thingy')
197         tree.add_command('server_list_lala', description='A testing server list', cmd_class=Shell)
198         tree.add_command('store_list_all', 'List all things', cmd_class=Command)
199         tree.add_command('store_list', 'List smthing pls', cmd_class=Shell)
200         tree.add_command('server_list', description='A server list subgrp')
201         tree.add_command('server', description='A server is a SERVER', cmd_class=CommandTree)
202         tree.set_class('server', None)
203         tree.set_description('server_list', '')
204         if tree.get_class('server_list_lala') is Shell:
205                 print('server_list_lala is Shell')
206         else:
207                 print('server_list_lala is not Shell')
208         tree.pretty_print()
209         print('store_list_all closest parent command is %s'%tree.get_closest_ancestor_command('store_list_all').path)
210         tree.set_class('store', tree.get_command('store_list').get_class())
211         tree.set_class('store_list', None)
212         print('store_list_all closest parent command is %s'%tree.get_closest_ancestor_command('store_list_all').path)
213         try:
214                 print('nonexisting_list_command closest parent is %s'%tree.get_closest_ancestor_command('nonexisting_list_command').path)
215         except KeyError:
216                 print('Aparrently nonexisting_list_command is nonexisting ')
217
218 class Shell(cmd.Cmd):
219         """Simple command processor example."""
220
221         def do_greet(self, line):
222                 """Hello [cmd]
223                         @line some line"""
224                 print "hello"
225
226         def do_lala(self, lala):
227                 print('This is what I got: %s'%lala)
228         def help_lala(self):
229                 print('This is SPAAARTAAAAAAA')
230
231         def do_lalum(self, args):
232                 print('lalum')
233         def complete_lalum(self, text, line, begidx, endidx):
234                 completions = ['lala']
235                 return completions
236
237         def do_EOF(self, line):
238                 return True
239
240 #sh = Shell()
241 #sh.prompt = 'lala_$ '
242 #sh.cmdloop()
243
244 #import sys
245 #sh.onecmd(' '.join(sys.argv[1:]))
246 #test_CommandTree()