Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / command_tree / __init__.py @ 16d7b9ff

History | View | Annotate | Download (6.6 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2012-2013 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 fd5db045 Stavros Sachtouris
35 00af4193 Stavros Sachtouris
class Command(object):
36 fd5db045 Stavros Sachtouris
    """Store a command and the next-level (2 levels)"""
37 fd5db045 Stavros Sachtouris
    _name = None
38 fd5db045 Stavros Sachtouris
    path = None
39 fd5db045 Stavros Sachtouris
    cmd_class = None
40 fd5db045 Stavros Sachtouris
    subcommands = {}
41 fd5db045 Stavros Sachtouris
    help = ' '
42 fd5db045 Stavros Sachtouris
43 38db356b Stavros Sachtouris
    def __init__(
44 38db356b Stavros Sachtouris
            self, path,
45 38db356b Stavros Sachtouris
            help=' ', subcommands={}, cmd_class=None, long_help=''):
46 2fde8651 Stavros Sachtouris
        assert path, 'Cannot initialize a command without a command path'
47 fd5db045 Stavros Sachtouris
        self.path = path
48 2fde8651 Stavros Sachtouris
        self.help = help or ''
49 2fde8651 Stavros Sachtouris
        self.subcommands = dict(subcommands) if subcommands else {}
50 2fde8651 Stavros Sachtouris
        self.cmd_class = cmd_class or None
51 38db356b Stavros Sachtouris
        self.long_help = '%s' % (long_help or '')
52 fd5db045 Stavros Sachtouris
53 fd5db045 Stavros Sachtouris
    @property
54 fd5db045 Stavros Sachtouris
    def name(self):
55 2fde8651 Stavros Sachtouris
        if not self._name:
56 fd5db045 Stavros Sachtouris
            self._name = self.path.split('_')[-1]
57 fd5db045 Stavros Sachtouris
        return str(self._name)
58 fd5db045 Stavros Sachtouris
59 fd5db045 Stavros Sachtouris
    def add_subcmd(self, subcmd):
60 fd5db045 Stavros Sachtouris
        if subcmd.path == '%s_%s' % (self.path, subcmd.name):
61 fd5db045 Stavros Sachtouris
            self.subcommands[subcmd.name] = subcmd
62 fd5db045 Stavros Sachtouris
            return True
63 fd5db045 Stavros Sachtouris
        return False
64 fd5db045 Stavros Sachtouris
65 fd5db045 Stavros Sachtouris
    def get_subcmd(self, name):
66 fd5db045 Stavros Sachtouris
        try:
67 fd5db045 Stavros Sachtouris
            return self.subcommands[name]
68 fd5db045 Stavros Sachtouris
        except KeyError:
69 fd5db045 Stavros Sachtouris
            return None
70 fd5db045 Stavros Sachtouris
71 fd5db045 Stavros Sachtouris
    def contains(self, name):
72 fd5db045 Stavros Sachtouris
        """Check if a name is a direct child of self"""
73 fd5db045 Stavros Sachtouris
        return name in self.subcommands
74 fd5db045 Stavros Sachtouris
75 fd5db045 Stavros Sachtouris
    @property
76 fd5db045 Stavros Sachtouris
    def is_command(self):
77 eb46e9a1 Stavros Sachtouris
        return len(self.subcommands) == 0 if self.cmd_class else False
78 fd5db045 Stavros Sachtouris
79 fd5db045 Stavros Sachtouris
    @property
80 fd5db045 Stavros Sachtouris
    def parent_path(self):
81 eb46e9a1 Stavros Sachtouris
        try:
82 9a22e094 Stavros Sachtouris
            return self.path[:self.path.rindex('_')]
83 eb46e9a1 Stavros Sachtouris
        except ValueError:
84 eb46e9a1 Stavros Sachtouris
            return ''
85 fd5db045 Stavros Sachtouris
86 fd5db045 Stavros Sachtouris
    def parse_out(self, args):
87 9a22e094 Stavros Sachtouris
        """Find the deepest subcommand matching a series of terms
88 9a22e094 Stavros Sachtouris
        but stop the first time a term doesn't match
89 9a22e094 Stavros Sachtouris

90 9a22e094 Stavros Sachtouris
        :param args: (list) terms to match commands against
91 9a22e094 Stavros Sachtouris

92 9a22e094 Stavros Sachtouris
        :returns: (parsed out command, the rest of the arguments)
93 9a22e094 Stavros Sachtouris

94 9a22e094 Stavros Sachtouris
        :raises TypeError: if args is not inalterable
95 9a22e094 Stavros Sachtouris
        """
96 fd5db045 Stavros Sachtouris
        cmd = self
97 fd5db045 Stavros Sachtouris
        index = 0
98 fd5db045 Stavros Sachtouris
        for term in args:
99 fd5db045 Stavros Sachtouris
            try:
100 fd5db045 Stavros Sachtouris
                cmd = cmd.subcommands[term]
101 fd5db045 Stavros Sachtouris
            except KeyError:
102 fd5db045 Stavros Sachtouris
                break
103 fd5db045 Stavros Sachtouris
            index += 1
104 fd5db045 Stavros Sachtouris
        return cmd, args[index:]
105 fd5db045 Stavros Sachtouris
106 fd5db045 Stavros Sachtouris
    def pretty_print(self, recursive=False):
107 d252a7a8 Stavros Sachtouris
        print('%s\t\t(Name: %s is_cmd: %s help: %s)' % (
108 d252a7a8 Stavros Sachtouris
            self.path, self.name, self.is_command, self.help))
109 eb46e9a1 Stavros Sachtouris
        for cmd in self.subcommands.values():
110 fd5db045 Stavros Sachtouris
            cmd.pretty_print(recursive)
111 fd5db045 Stavros Sachtouris
112 00af4193 Stavros Sachtouris
113 d9325478 Stavros Sachtouris
class CommandTree(object):
114 d9325478 Stavros Sachtouris
115 38db356b Stavros Sachtouris
    def __init__(self, name, description='', long_description=''):
116 fd5db045 Stavros Sachtouris
        self.name = name
117 fd5db045 Stavros Sachtouris
        self.description = description
118 38db356b Stavros Sachtouris
        self.long_description = '%s' % (long_description or '')
119 d252a7a8 Stavros Sachtouris
        self.groups = dict()
120 d252a7a8 Stavros Sachtouris
        self._all_commands = dict()
121 fd5db045 Stavros Sachtouris
122 320aac17 Stavros Sachtouris
    def exclude(self, groups_to_exclude=[]):
123 320aac17 Stavros Sachtouris
        for group in groups_to_exclude:
124 320aac17 Stavros Sachtouris
            self.groups.pop(group, None)
125 320aac17 Stavros Sachtouris
126 38db356b Stavros Sachtouris
    def add_command(
127 38db356b Stavros Sachtouris
            self, command_path,
128 38db356b Stavros Sachtouris
            description=None, cmd_class=None, long_description=''):
129 fd5db045 Stavros Sachtouris
        terms = command_path.split('_')
130 fd5db045 Stavros Sachtouris
        try:
131 fd5db045 Stavros Sachtouris
            cmd = self.groups[terms[0]]
132 fd5db045 Stavros Sachtouris
        except KeyError:
133 fd5db045 Stavros Sachtouris
            cmd = Command(terms[0])
134 fd5db045 Stavros Sachtouris
            self.groups[terms[0]] = cmd
135 fd5db045 Stavros Sachtouris
            self._all_commands[terms[0]] = cmd
136 fd5db045 Stavros Sachtouris
        path = terms[0]
137 fd5db045 Stavros Sachtouris
        for term in terms[1:]:
138 fd5db045 Stavros Sachtouris
            path += '_' + term
139 fd5db045 Stavros Sachtouris
            try:
140 fd5db045 Stavros Sachtouris
                cmd = cmd.subcommands[term]
141 fd5db045 Stavros Sachtouris
            except KeyError:
142 fd5db045 Stavros Sachtouris
                new_cmd = Command(path)
143 fd5db045 Stavros Sachtouris
                self._all_commands[path] = new_cmd
144 fd5db045 Stavros Sachtouris
                cmd.add_subcmd(new_cmd)
145 fd5db045 Stavros Sachtouris
                cmd = new_cmd
146 d252a7a8 Stavros Sachtouris
        cmd.cmd_class = cmd_class or None
147 d252a7a8 Stavros Sachtouris
        cmd.help = description or None
148 38db356b Stavros Sachtouris
        cmd.long_help = long_description or cmd.long_help
149 fd5db045 Stavros Sachtouris
150 fce93ff6 Stavros Sachtouris
    def find_best_match(self, terms):
151 fce93ff6 Stavros Sachtouris
        """Find a command that best matches a given list of terms
152 fce93ff6 Stavros Sachtouris

153 16d7b9ff Stavros Sachtouris
        :param terms: (list of str) match against paths in cmd_tree, e.g.,
154 d252a7a8 Stavros Sachtouris
            ['aa', 'bb', 'cc'] matches aa_bb_cc
155 fce93ff6 Stavros Sachtouris

156 d252a7a8 Stavros Sachtouris
        :returns: (Command, list) the matching command, the remaining terms or
157 d252a7a8 Stavros Sachtouris
            None
158 fce93ff6 Stavros Sachtouris
        """
159 fce93ff6 Stavros Sachtouris
        path = []
160 fce93ff6 Stavros Sachtouris
        for term in terms:
161 fce93ff6 Stavros Sachtouris
            check_path = path + [term]
162 fce93ff6 Stavros Sachtouris
            if '_'.join(check_path) not in self._all_commands:
163 fce93ff6 Stavros Sachtouris
                break
164 fce93ff6 Stavros Sachtouris
            path = check_path
165 fce93ff6 Stavros Sachtouris
        if path:
166 fce93ff6 Stavros Sachtouris
            return (self._all_commands['_'.join(path)], terms[len(path):])
167 fce93ff6 Stavros Sachtouris
        return (None, terms)
168 fce93ff6 Stavros Sachtouris
169 6514457a Stavros Sachtouris
    def add_tree(self, new_tree):
170 6514457a Stavros Sachtouris
        tname = new_tree.name
171 6514457a Stavros Sachtouris
        tdesc = new_tree.description
172 6514457a Stavros Sachtouris
        self.groups.update(new_tree.groups)
173 6514457a Stavros Sachtouris
        self._all_commands.update(new_tree._all_commands)
174 d252a7a8 Stavros Sachtouris
        try:
175 d252a7a8 Stavros Sachtouris
            self._all_commands[tname].help = tdesc
176 d252a7a8 Stavros Sachtouris
        except KeyError:
177 d252a7a8 Stavros Sachtouris
            self.add_command(tname, tdesc)
178 6514457a Stavros Sachtouris
179 0d249b3e Stavros Sachtouris
    def has_command(self, path):
180 0d249b3e Stavros Sachtouris
        return path in self._all_commands
181 0d249b3e Stavros Sachtouris
182 fd5db045 Stavros Sachtouris
    def get_command(self, path):
183 fd5db045 Stavros Sachtouris
        return self._all_commands[path]
184 fd5db045 Stavros Sachtouris
185 d252a7a8 Stavros Sachtouris
    def subnames(self, path=None):
186 de73876b Stavros Sachtouris
        if path in (None, ''):
187 d252a7a8 Stavros Sachtouris
            return self.groups.keys()
188 eb46e9a1 Stavros Sachtouris
        return self._all_commands[path].subcommands.keys()
189 fd5db045 Stavros Sachtouris
190 fd5db045 Stavros Sachtouris
    def get_subcommands(self, path=None):
191 eb46e9a1 Stavros Sachtouris
        return self._all_commands[path].subcommands.values() if (
192 d252a7a8 Stavros Sachtouris
            path) else self.groups.values()
193 fd5db045 Stavros Sachtouris
194 fd5db045 Stavros Sachtouris
    def pretty_print(self, group=None):
195 fd5db045 Stavros Sachtouris
        if group is None:
196 fd5db045 Stavros Sachtouris
            for group in self.groups:
197 fd5db045 Stavros Sachtouris
                self.pretty_print(group)
198 fd5db045 Stavros Sachtouris
        else:
199 fd5db045 Stavros Sachtouris
            self.groups[group].pretty_print(recursive=True)