Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / command_tree.py @ ed9af02c

History | View | Annotate | Download (7.6 kB)

1 b9331a9f Stavros Sachtouris
# Copyright 2012 GRNET S.A. All rights reserved.
2 00af4193 Stavros Sachtouris
#
3 00af4193 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 00af4193 Stavros Sachtouris
# without modification, are permitted provided that the following
5 00af4193 Stavros Sachtouris
# conditions are met:
6 00af4193 Stavros Sachtouris
#
7 00af4193 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 fd5db045 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 fd5db045 Stavros Sachtouris
#      disclaimer.
10 00af4193 Stavros Sachtouris
#
11 00af4193 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 fd5db045 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 fd5db045 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 fd5db045 Stavros Sachtouris
#      provided with the distribution.
15 00af4193 Stavros Sachtouris
#
16 00af4193 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 00af4193 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 00af4193 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 00af4193 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 00af4193 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 00af4193 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 00af4193 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 00af4193 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 00af4193 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 00af4193 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 00af4193 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 00af4193 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 00af4193 Stavros Sachtouris
#
29 00af4193 Stavros Sachtouris
# The views and conclusions contained in the software and
30 00af4193 Stavros Sachtouris
# documentation are those of the authors and should not be
31 00af4193 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 00af4193 Stavros Sachtouris
# or implied, of GRNET S.A.
33 00af4193 Stavros Sachtouris
34 34b88989 Stavros Sachtouris
from kamaki.clients import Client
35 34b88989 Stavros Sachtouris
36 fd5db045 Stavros Sachtouris
37 00af4193 Stavros Sachtouris
class Command(object):
38 fd5db045 Stavros Sachtouris
    """Store a command and the next-level (2 levels)"""
39 fd5db045 Stavros Sachtouris
    _name = None
40 fd5db045 Stavros Sachtouris
    path = None
41 fd5db045 Stavros Sachtouris
    cmd_class = None
42 fd5db045 Stavros Sachtouris
    subcommands = {}
43 fd5db045 Stavros Sachtouris
    help = ' '
44 fd5db045 Stavros Sachtouris
45 fd5db045 Stavros Sachtouris
    def __init__(self, path, help=' ', subcommands={}, cmd_class=None):
46 fd5db045 Stavros Sachtouris
        self.path = path
47 fd5db045 Stavros Sachtouris
        self.help = help
48 fd5db045 Stavros Sachtouris
        self.subcommands = dict(subcommands)
49 fd5db045 Stavros Sachtouris
        self.cmd_class = cmd_class
50 fd5db045 Stavros Sachtouris
51 fd5db045 Stavros Sachtouris
    @property
52 fd5db045 Stavros Sachtouris
    def name(self):
53 fd5db045 Stavros Sachtouris
        if self._name is None:
54 fd5db045 Stavros Sachtouris
            self._name = self.path.split('_')[-1]
55 fd5db045 Stavros Sachtouris
        return str(self._name)
56 fd5db045 Stavros Sachtouris
57 fd5db045 Stavros Sachtouris
    def add_subcmd(self, subcmd):
58 fd5db045 Stavros Sachtouris
        if subcmd.path == '%s_%s' % (self.path, subcmd.name):
59 fd5db045 Stavros Sachtouris
            self.subcommands[subcmd.name] = subcmd
60 fd5db045 Stavros Sachtouris
            return True
61 fd5db045 Stavros Sachtouris
        return False
62 fd5db045 Stavros Sachtouris
63 fd5db045 Stavros Sachtouris
    def get_subcmd(self, name):
64 fd5db045 Stavros Sachtouris
        try:
65 fd5db045 Stavros Sachtouris
            return self.subcommands[name]
66 fd5db045 Stavros Sachtouris
        except KeyError:
67 fd5db045 Stavros Sachtouris
            return None
68 fd5db045 Stavros Sachtouris
69 fd5db045 Stavros Sachtouris
    def contains(self, name):
70 fd5db045 Stavros Sachtouris
        """Check if a name is a direct child of self"""
71 fd5db045 Stavros Sachtouris
        return name in self.subcommands
72 fd5db045 Stavros Sachtouris
73 fd5db045 Stavros Sachtouris
    @property
74 fd5db045 Stavros Sachtouris
    def is_command(self):
75 8741c407 Stavros Sachtouris
        return self.cmd_class is not None and len(self.subcommands) == 0
76 fd5db045 Stavros Sachtouris
77 fd5db045 Stavros Sachtouris
    @property
78 fd5db045 Stavros Sachtouris
    def has_description(self):
79 fd5db045 Stavros Sachtouris
        return len(self.help.strip()) > 0
80 fd5db045 Stavros Sachtouris
81 fd5db045 Stavros Sachtouris
    @property
82 fd5db045 Stavros Sachtouris
    def description(self):
83 fd5db045 Stavros Sachtouris
        return self.help
84 fd5db045 Stavros Sachtouris
85 fd5db045 Stavros Sachtouris
    @property
86 fd5db045 Stavros Sachtouris
    def parent_path(self):
87 fd5db045 Stavros Sachtouris
        parentpath, sep, name = self.path.rpartition('_')
88 fd5db045 Stavros Sachtouris
        return parentpath
89 fd5db045 Stavros Sachtouris
90 fd5db045 Stavros Sachtouris
    def set_class(self, cmd_class):
91 fd5db045 Stavros Sachtouris
        self.cmd_class = cmd_class
92 fd5db045 Stavros Sachtouris
93 fd5db045 Stavros Sachtouris
    def get_class(self):
94 fd5db045 Stavros Sachtouris
        return self.cmd_class
95 fd5db045 Stavros Sachtouris
96 fd5db045 Stavros Sachtouris
    def has_subname(self, subname):
97 fd5db045 Stavros Sachtouris
        return subname in self.subcommands
98 fd5db045 Stavros Sachtouris
99 fd5db045 Stavros Sachtouris
    def get_subnames(self):
100 fd5db045 Stavros Sachtouris
        return self.subcommands.keys()
101 fd5db045 Stavros Sachtouris
102 fd5db045 Stavros Sachtouris
    def get_subcommands(self):
103 fd5db045 Stavros Sachtouris
        return self.subcommands.values()
104 fd5db045 Stavros Sachtouris
105 fd5db045 Stavros Sachtouris
    def sublen(self):
106 fd5db045 Stavros Sachtouris
        return len(self.subcommands)
107 fd5db045 Stavros Sachtouris
108 fd5db045 Stavros Sachtouris
    def parse_out(self, args):
109 fd5db045 Stavros Sachtouris
        cmd = self
110 fd5db045 Stavros Sachtouris
        index = 0
111 fd5db045 Stavros Sachtouris
        for term in args:
112 fd5db045 Stavros Sachtouris
            try:
113 fd5db045 Stavros Sachtouris
                cmd = cmd.subcommands[term]
114 fd5db045 Stavros Sachtouris
            except KeyError:
115 fd5db045 Stavros Sachtouris
                break
116 fd5db045 Stavros Sachtouris
            index += 1
117 fd5db045 Stavros Sachtouris
        return cmd, args[index:]
118 fd5db045 Stavros Sachtouris
119 fd5db045 Stavros Sachtouris
    def pretty_print(self, recursive=False):
120 de73876b Stavros Sachtouris
        print('Path: %s (Name: %s) is_cmd: %s\n\thelp: %s' % (
121 de73876b Stavros Sachtouris
            self.path,
122 de73876b Stavros Sachtouris
            self.name,
123 de73876b Stavros Sachtouris
            self.is_command,
124 de73876b Stavros Sachtouris
            self.help))
125 fd5db045 Stavros Sachtouris
        for cmd in self.get_subcommands():
126 fd5db045 Stavros Sachtouris
            cmd.pretty_print(recursive)
127 fd5db045 Stavros Sachtouris
128 00af4193 Stavros Sachtouris
129 d9325478 Stavros Sachtouris
class CommandTree(object):
130 d9325478 Stavros Sachtouris
131 fd5db045 Stavros Sachtouris
    groups = {}
132 fd5db045 Stavros Sachtouris
    _all_commands = {}
133 fd5db045 Stavros Sachtouris
    name = None
134 fd5db045 Stavros Sachtouris
    description = None
135 fd5db045 Stavros Sachtouris
136 fd5db045 Stavros Sachtouris
    def __init__(self, name, description=''):
137 fd5db045 Stavros Sachtouris
        self.name = name
138 fd5db045 Stavros Sachtouris
        self.description = description
139 fd5db045 Stavros Sachtouris
140 fd5db045 Stavros Sachtouris
    def add_command(self, command_path, description=None, cmd_class=None):
141 fd5db045 Stavros Sachtouris
        terms = command_path.split('_')
142 fd5db045 Stavros Sachtouris
        try:
143 fd5db045 Stavros Sachtouris
            cmd = self.groups[terms[0]]
144 fd5db045 Stavros Sachtouris
        except KeyError:
145 fd5db045 Stavros Sachtouris
            cmd = Command(terms[0])
146 fd5db045 Stavros Sachtouris
            self.groups[terms[0]] = cmd
147 fd5db045 Stavros Sachtouris
            self._all_commands[terms[0]] = cmd
148 fd5db045 Stavros Sachtouris
        path = terms[0]
149 fd5db045 Stavros Sachtouris
        for term in terms[1:]:
150 fd5db045 Stavros Sachtouris
            path += '_' + term
151 fd5db045 Stavros Sachtouris
            try:
152 fd5db045 Stavros Sachtouris
                cmd = cmd.subcommands[term]
153 fd5db045 Stavros Sachtouris
            except KeyError:
154 fd5db045 Stavros Sachtouris
                new_cmd = Command(path)
155 fd5db045 Stavros Sachtouris
                self._all_commands[path] = new_cmd
156 fd5db045 Stavros Sachtouris
                cmd.add_subcmd(new_cmd)
157 fd5db045 Stavros Sachtouris
                cmd = new_cmd
158 8741c407 Stavros Sachtouris
        if cmd_class:
159 fd5db045 Stavros Sachtouris
            cmd.set_class(cmd_class)
160 fd5db045 Stavros Sachtouris
        if description is not None:
161 fd5db045 Stavros Sachtouris
            cmd.help = description
162 fd5db045 Stavros Sachtouris
163 fce93ff6 Stavros Sachtouris
    def find_best_match(self, terms):
164 fce93ff6 Stavros Sachtouris
        """Find a command that best matches a given list of terms
165 fce93ff6 Stavros Sachtouris

166 fce93ff6 Stavros Sachtouris
        :param terms: (list of str) match them against paths in cmd_tree
167 fce93ff6 Stavros Sachtouris

168 fce93ff6 Stavros Sachtouris
        :returns: (Command, list) the matching command, the remaining terms
169 fce93ff6 Stavros Sachtouris
        """
170 fce93ff6 Stavros Sachtouris
        path = []
171 fce93ff6 Stavros Sachtouris
        for term in terms:
172 fce93ff6 Stavros Sachtouris
            check_path = path + [term]
173 fce93ff6 Stavros Sachtouris
            if '_'.join(check_path) not in self._all_commands:
174 fce93ff6 Stavros Sachtouris
                break
175 fce93ff6 Stavros Sachtouris
            path = check_path
176 fce93ff6 Stavros Sachtouris
        if path:
177 fce93ff6 Stavros Sachtouris
            return (self._all_commands['_'.join(path)], terms[len(path):])
178 fce93ff6 Stavros Sachtouris
        return (None, terms)
179 fce93ff6 Stavros Sachtouris
180 6514457a Stavros Sachtouris
    def add_tree(self, new_tree):
181 6514457a Stavros Sachtouris
        tname = new_tree.name
182 6514457a Stavros Sachtouris
        tdesc = new_tree.description
183 6514457a Stavros Sachtouris
        self.groups.update(new_tree.groups)
184 6514457a Stavros Sachtouris
        self._all_commands.update(new_tree._all_commands)
185 6514457a Stavros Sachtouris
        self.set_description(tname, tdesc)
186 6514457a Stavros Sachtouris
187 0d249b3e Stavros Sachtouris
    def has_command(self, path):
188 0d249b3e Stavros Sachtouris
        return path in self._all_commands
189 0d249b3e Stavros Sachtouris
190 fd5db045 Stavros Sachtouris
    def get_command(self, path):
191 fd5db045 Stavros Sachtouris
        return self._all_commands[path]
192 fd5db045 Stavros Sachtouris
193 fd5db045 Stavros Sachtouris
    def get_groups(self):
194 fd5db045 Stavros Sachtouris
        return self.groups.values()
195 fd5db045 Stavros Sachtouris
196 fd5db045 Stavros Sachtouris
    def get_group_names(self):
197 fd5db045 Stavros Sachtouris
        return self.groups.keys()
198 fd5db045 Stavros Sachtouris
199 fd5db045 Stavros Sachtouris
    def set_description(self, path, description):
200 fd5db045 Stavros Sachtouris
        self._all_commands[path].help = description
201 fd5db045 Stavros Sachtouris
202 d07b3796 Stavros Sachtouris
    def get_description(self, path):
203 fd5db045 Stavros Sachtouris
        return self._all_commands[path].help
204 fd5db045 Stavros Sachtouris
205 fd5db045 Stavros Sachtouris
    def set_class(self, path, cmd_class):
206 fd5db045 Stavros Sachtouris
        self._all_commands[path].set_class(cmd_class)
207 fd5db045 Stavros Sachtouris
208 fd5db045 Stavros Sachtouris
    def get_class(self, path):
209 fd5db045 Stavros Sachtouris
        return self._all_commands[path].get_class()
210 fd5db045 Stavros Sachtouris
211 fd5db045 Stavros Sachtouris
    def get_subnames(self, path=None):
212 de73876b Stavros Sachtouris
        if path in (None, ''):
213 de73876b Stavros Sachtouris
            return self.get_group_names()
214 de73876b Stavros Sachtouris
        return self._all_commands[path].get_subnames()
215 fd5db045 Stavros Sachtouris
216 fd5db045 Stavros Sachtouris
    def get_subcommands(self, path=None):
217 de73876b Stavros Sachtouris
        if path in (None, ''):
218 de73876b Stavros Sachtouris
            return self.get_groups()
219 de73876b Stavros Sachtouris
        return self._all_commands[path].get_subcommands()
220 fd5db045 Stavros Sachtouris
221 fd5db045 Stavros Sachtouris
    def get_parent(self, path):
222 fd5db045 Stavros Sachtouris
        if '_' not in path:
223 fd5db045 Stavros Sachtouris
            return None
224 fd5db045 Stavros Sachtouris
        terms = path.split('_')
225 fd5db045 Stavros Sachtouris
        parent_path = '_'.join(terms[:-1])
226 fd5db045 Stavros Sachtouris
        return self._all_commands[parent_path]
227 fd5db045 Stavros Sachtouris
228 fd5db045 Stavros Sachtouris
    def get_closest_ancestor_command(self, path):
229 fd5db045 Stavros Sachtouris
        path, sep, name = path.rpartition('_')
230 fd5db045 Stavros Sachtouris
        while len(path) > 0:
231 fd5db045 Stavros Sachtouris
            cmd = self._all_commands[path]
232 fd5db045 Stavros Sachtouris
            if cmd.is_command:
233 fd5db045 Stavros Sachtouris
                return cmd
234 fd5db045 Stavros Sachtouris
            path, sep, name = path.rpartition('_')
235 fd5db045 Stavros Sachtouris
        return None
236 fd5db045 Stavros Sachtouris
237 fd5db045 Stavros Sachtouris
        if '_' not in path:
238 fd5db045 Stavros Sachtouris
            return None
239 3dabe5d2 Stavros Sachtouris
        terms = path.split()[:-1]
240 fd5db045 Stavros Sachtouris
        while len(terms) > 0:
241 fd5db045 Stavros Sachtouris
            tmp_path = '_'.join(terms)
242 fd5db045 Stavros Sachtouris
            cmd = self._all_commands[tmp_path]
243 fd5db045 Stavros Sachtouris
            if cmd.is_command:
244 fd5db045 Stavros Sachtouris
                return cmd
245 fd5db045 Stavros Sachtouris
            terms = terms[:-1]
246 fd5db045 Stavros Sachtouris
        raise KeyError('No ancestor commands')
247 fd5db045 Stavros Sachtouris
248 fd5db045 Stavros Sachtouris
    def pretty_print(self, group=None):
249 fd5db045 Stavros Sachtouris
        if group is None:
250 fd5db045 Stavros Sachtouris
            for group in self.groups:
251 fd5db045 Stavros Sachtouris
                self.pretty_print(group)
252 fd5db045 Stavros Sachtouris
        else:
253 fd5db045 Stavros Sachtouris
            self.groups[group].pretty_print(recursive=True)