Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument.py @ 852a22e7

History | View | Annotate | Download (9.4 kB)

1 b9331a9f Stavros Sachtouris
# Copyright 2012 GRNET S.A. All rights reserved.
2 b9331a9f Stavros Sachtouris
#
3 b9331a9f Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 b9331a9f Stavros Sachtouris
# without modification, are permitted provided that the following
5 b9331a9f Stavros Sachtouris
# conditions are met:
6 b9331a9f Stavros Sachtouris
#
7 b9331a9f Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 b9331a9f Stavros Sachtouris
#     copyright notice, this list of conditions and the following
9 b9331a9f Stavros Sachtouris
#     disclaimer.
10 b9331a9f Stavros Sachtouris
#
11 b9331a9f Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 b9331a9f Stavros Sachtouris
#     copyright notice, this list of conditions and the following
13 b9331a9f Stavros Sachtouris
#     disclaimer in the documentation and/or other materials
14 b9331a9f Stavros Sachtouris
#     provided with the distribution.
15 b9331a9f Stavros Sachtouris
#
16 b9331a9f Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 b9331a9f Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 b9331a9f Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 b9331a9f Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 b9331a9f Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 b9331a9f Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 b9331a9f Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 b9331a9f Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 b9331a9f Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 b9331a9f Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 b9331a9f Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 b9331a9f Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 b9331a9f Stavros Sachtouris
#
29 b9331a9f Stavros Sachtouris
# The views and conclusions contained in the software and
30 b9331a9f Stavros Sachtouris
# documentation are those of the authors and should not be
31 b9331a9f Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 b9331a9f Stavros Sachtouris
# or implied, of GRNET S.A.
33 9bdc89da Stavros Sachtouris
34 c270fe96 Stavros Sachtouris
from kamaki.cli.config import Config
35 c270fe96 Stavros Sachtouris
from kamaki.cli.errors import CLISyntaxError
36 af6de846 Stavros Sachtouris
from argparse import ArgumentParser, ArgumentError
37 dfee2caf Stavros Sachtouris
38 fd1f1d96 Stavros Sachtouris
try:
39 fd1f1d96 Stavros Sachtouris
    from progress.bar import IncrementalBar
40 fd1f1d96 Stavros Sachtouris
except ImportError:
41 fd1f1d96 Stavros Sachtouris
    # progress not installed - pls, pip install progress
42 fd1f1d96 Stavros Sachtouris
    pass
43 fd1f1d96 Stavros Sachtouris
44 fd5db045 Stavros Sachtouris
45 dfee2caf Stavros Sachtouris
class Argument(object):
46 dfee2caf Stavros Sachtouris
    """An argument that can be parsed from command line or otherwise"""
47 dfee2caf Stavros Sachtouris
48 dfee2caf Stavros Sachtouris
    def __init__(self, arity, help=None, parsed_name=None, default=None):
49 dfee2caf Stavros Sachtouris
        self.arity = int(arity)
50 dfee2caf Stavros Sachtouris
51 dfee2caf Stavros Sachtouris
        if help is not None:
52 dfee2caf Stavros Sachtouris
            self.help = help
53 dfee2caf Stavros Sachtouris
        if parsed_name is not None:
54 dfee2caf Stavros Sachtouris
            self.parsed_name = parsed_name
55 dfee2caf Stavros Sachtouris
        if default is not None:
56 dfee2caf Stavros Sachtouris
            self.default = default
57 dfee2caf Stavros Sachtouris
58 fd5db045 Stavros Sachtouris
    @property
59 dfee2caf Stavros Sachtouris
    def parsed_name(self):
60 dfee2caf Stavros Sachtouris
        return getattr(self, '_parsed_name', None)
61 fd5db045 Stavros Sachtouris
62 dfee2caf Stavros Sachtouris
    @parsed_name.setter
63 dfee2caf Stavros Sachtouris
    def parsed_name(self, newname):
64 dfee2caf Stavros Sachtouris
        self._parsed_name = getattr(self, '_parsed_name', [])
65 dfee2caf Stavros Sachtouris
        if isinstance(newname, list) or isinstance(newname, tuple):
66 dfee2caf Stavros Sachtouris
            self._parsed_name += list(newname)
67 dfee2caf Stavros Sachtouris
        else:
68 dfee2caf Stavros Sachtouris
            self._parsed_name.append(unicode(newname))
69 dfee2caf Stavros Sachtouris
70 fd5db045 Stavros Sachtouris
    @property
71 dfee2caf Stavros Sachtouris
    def help(self):
72 dfee2caf Stavros Sachtouris
        return getattr(self, '_help', None)
73 fd5db045 Stavros Sachtouris
74 dfee2caf Stavros Sachtouris
    @help.setter
75 dfee2caf Stavros Sachtouris
    def help(self, newhelp):
76 dfee2caf Stavros Sachtouris
        self._help = unicode(newhelp)
77 dfee2caf Stavros Sachtouris
78 fd5db045 Stavros Sachtouris
    @property
79 dfee2caf Stavros Sachtouris
    def arity(self):
80 dfee2caf Stavros Sachtouris
        return getattr(self, '_arity', None)
81 fd5db045 Stavros Sachtouris
82 dfee2caf Stavros Sachtouris
    @arity.setter
83 dfee2caf Stavros Sachtouris
    def arity(self, newarity):
84 dfee2caf Stavros Sachtouris
        newarity = int(newarity)
85 dfee2caf Stavros Sachtouris
        self._arity = newarity
86 dfee2caf Stavros Sachtouris
87 fd5db045 Stavros Sachtouris
    @property
88 dfee2caf Stavros Sachtouris
    def default(self):
89 dfee2caf Stavros Sachtouris
        if not hasattr(self, '_default'):
90 dfee2caf Stavros Sachtouris
            self._default = False if self.arity == 0 else None
91 dfee2caf Stavros Sachtouris
        return self._default
92 fd5db045 Stavros Sachtouris
93 dfee2caf Stavros Sachtouris
    @default.setter
94 dfee2caf Stavros Sachtouris
    def default(self, newdefault):
95 dfee2caf Stavros Sachtouris
        self._default = newdefault
96 dfee2caf Stavros Sachtouris
97 fd5db045 Stavros Sachtouris
    @property
98 dfee2caf Stavros Sachtouris
    def value(self):
99 dfee2caf Stavros Sachtouris
        return getattr(self, '_value', self.default)
100 fd5db045 Stavros Sachtouris
101 dfee2caf Stavros Sachtouris
    @value.setter
102 dfee2caf Stavros Sachtouris
    def value(self, newvalue):
103 dfee2caf Stavros Sachtouris
        self._value = newvalue
104 dfee2caf Stavros Sachtouris
105 dfee2caf Stavros Sachtouris
    def update_parser(self, parser, name):
106 dfee2caf Stavros Sachtouris
        """Update an argument parser with this argument info"""
107 0a0b9fb6 Stavros Sachtouris
        action = 'append' if self.arity < 0\
108 0a0b9fb6 Stavros Sachtouris
            else 'store_true' if self.arity == 0\
109 0a0b9fb6 Stavros Sachtouris
            else 'store'
110 dfee2caf Stavros Sachtouris
        parser.add_argument(*self.parsed_name, dest=name, action=action,
111 dfee2caf Stavros Sachtouris
            default=self.default, help=self.help)
112 dfee2caf Stavros Sachtouris
113 dfee2caf Stavros Sachtouris
    def main(self):
114 dfee2caf Stavros Sachtouris
        """Overide this method to give functionality to ur args"""
115 dfee2caf Stavros Sachtouris
        raise NotImplementedError
116 dfee2caf Stavros Sachtouris
117 fd5db045 Stavros Sachtouris
118 9bdc89da Stavros Sachtouris
class ConfigArgument(Argument):
119 fd5db045 Stavros Sachtouris
    @property
120 c41a86b2 Stavros Sachtouris
    def value(self):
121 d486baec Stavros Sachtouris
        super(self.__class__, self).value
122 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
123 fd5db045 Stavros Sachtouris
124 c41a86b2 Stavros Sachtouris
    @value.setter
125 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
126 fd5db045 Stavros Sachtouris
        self._value = Config(config_file) if config_file else Config()
127 9bdc89da Stavros Sachtouris
128 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
129 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
130 c41a86b2 Stavros Sachtouris
131 c41a86b2 Stavros Sachtouris
    def get_groups(self):
132 c41a86b2 Stavros Sachtouris
        return self.value.apis()
133 017d37ce Stavros Sachtouris
134 017d37ce Stavros Sachtouris
_config_arg = ConfigArgument(1, 'Path to configuration file', '--config')
135 017d37ce Stavros Sachtouris
136 fd5db045 Stavros Sachtouris
137 9bdc89da Stavros Sachtouris
class CmdLineConfigArgument(Argument):
138 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
139 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
140 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
141 c41a86b2 Stavros Sachtouris
142 fd5db045 Stavros Sachtouris
    @property
143 c41a86b2 Stavros Sachtouris
    def value(self):
144 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
145 fd5db045 Stavros Sachtouris
146 c41a86b2 Stavros Sachtouris
    @value.setter
147 c41a86b2 Stavros Sachtouris
    def value(self, options):
148 c41a86b2 Stavros Sachtouris
        if options == self.default:
149 c41a86b2 Stavros Sachtouris
            return
150 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
151 fd5db045 Stavros Sachtouris
            options = [unicode(options)]
152 c41a86b2 Stavros Sachtouris
        for option in options:
153 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
154 c41a86b2 Stavros Sachtouris
            if not sep:
155 0a0b9fb6 Stavros Sachtouris
                raise CLISyntaxError('Argument Syntax Error ',
156 71882bca Stavros Sachtouris
                    details='%s is missing a "=" (usage: -o section.key=val)'\
157 0a0b9fb6 Stavros Sachtouris
                        % option)
158 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
159 0a0b9fb6 Stavros Sachtouris
        if not sep:
160 0a0b9fb6 Stavros Sachtouris
            key = section
161 0a0b9fb6 Stavros Sachtouris
            section = 'global'
162 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
163 0a0b9fb6 Stavros Sachtouris
            section.strip(),
164 fd5db045 Stavros Sachtouris
            key.strip(),
165 fd5db045 Stavros Sachtouris
            val.strip())
166 fd5db045 Stavros Sachtouris
167 c41a86b2 Stavros Sachtouris
168 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
169 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
170 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
171 c41a86b2 Stavros Sachtouris
172 fd5db045 Stavros Sachtouris
173 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
174 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
175 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
176 c41a86b2 Stavros Sachtouris
177 fd5db045 Stavros Sachtouris
178 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
179 fd5db045 Stavros Sachtouris
    @property
180 e3d4d442 Stavros Sachtouris
    def value(self):
181 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
182 fd5db045 Stavros Sachtouris
183 e3d4d442 Stavros Sachtouris
    @value.setter
184 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
185 e3d4d442 Stavros Sachtouris
        if newvalue == self.default:
186 e3d4d442 Stavros Sachtouris
            self._value = self.default
187 e3d4d442 Stavros Sachtouris
            return
188 e3d4d442 Stavros Sachtouris
        try:
189 e3d4d442 Stavros Sachtouris
            self._value = int(newvalue)
190 e3d4d442 Stavros Sachtouris
        except ValueError:
191 fd5db045 Stavros Sachtouris
            raise CLISyntaxError('IntArgument Error',
192 fd5db045 Stavros Sachtouris
                details='Value %s not an int' % newvalue)
193 fd5db045 Stavros Sachtouris
194 e3d4d442 Stavros Sachtouris
195 c41a86b2 Stavros Sachtouris
class VersionArgument(FlagArgument):
196 fd5db045 Stavros Sachtouris
    @property
197 c41a86b2 Stavros Sachtouris
    def value(self):
198 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
199 fd5db045 Stavros Sachtouris
200 c41a86b2 Stavros Sachtouris
    @value.setter
201 c41a86b2 Stavros Sachtouris
    def value(self, newvalue):
202 c41a86b2 Stavros Sachtouris
        self._value = newvalue
203 c41a86b2 Stavros Sachtouris
        self.main()
204 c41a86b2 Stavros Sachtouris
205 c41a86b2 Stavros Sachtouris
    def main(self):
206 c41a86b2 Stavros Sachtouris
        if self.value:
207 c41a86b2 Stavros Sachtouris
            import kamaki
208 fd5db045 Stavros Sachtouris
            print('kamaki %s' % kamaki.__version__)
209 fd5db045 Stavros Sachtouris
210 9bdc89da Stavros Sachtouris
211 0a0b9fb6 Stavros Sachtouris
class KeyValueArgument(Argument):
212 0a0b9fb6 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
213 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
214 f3e94e06 Stavros Sachtouris
215 fd5db045 Stavros Sachtouris
    @property
216 f3e94e06 Stavros Sachtouris
    def value(self):
217 f3e94e06 Stavros Sachtouris
        return super(KeyValueArgument, self).value
218 fd5db045 Stavros Sachtouris
219 fd5db045 Stavros Sachtouris
    @value.setter
220 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
221 0a0b9fb6 Stavros Sachtouris
        self._value = {}
222 f3e94e06 Stavros Sachtouris
        for pair in keyvalue_pairs:
223 fd5db045 Stavros Sachtouris
            key, sep, val = pair.partition('=')
224 f3e94e06 Stavros Sachtouris
            if not sep:
225 0a0b9fb6 Stavros Sachtouris
                raise CLISyntaxError('Argument syntax error ',
226 0a0b9fb6 Stavros Sachtouris
                    details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
227 23803b28 Stavros Sachtouris
            self._value[key.strip()] = val.strip()
228 f3e94e06 Stavros Sachtouris
229 fd1f1d96 Stavros Sachtouris
230 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
231 fd1f1d96 Stavros Sachtouris
232 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
233 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
234 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
235 fd1f1d96 Stavros Sachtouris
        try:
236 852a22e7 Stavros Sachtouris
            IncrementalBar
237 fd1f1d96 Stavros Sachtouris
        except NameError:
238 fd1f1d96 Stavros Sachtouris
            print('Waring: no progress bar functionality')
239 fd1f1d96 Stavros Sachtouris
240 852a22e7 Stavros Sachtouris
    def clone(self):
241 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
242 852a22e7 Stavros Sachtouris
            self.help,
243 852a22e7 Stavros Sachtouris
            self.parsed_name,
244 852a22e7 Stavros Sachtouris
            self.default)
245 852a22e7 Stavros Sachtouris
        newarg._value = self._value
246 852a22e7 Stavros Sachtouris
        return newarg
247 852a22e7 Stavros Sachtouris
248 fd1f1d96 Stavros Sachtouris
    def get_generator(self, message, message_len=25):
249 fd1f1d96 Stavros Sachtouris
        if self.value:
250 fd1f1d96 Stavros Sachtouris
            return None
251 852a22e7 Stavros Sachtouris
        self.bar = IncrementalBar()
252 fd1f1d96 Stavros Sachtouris
        try:
253 852a22e7 Stavros Sachtouris
            self.bar.message = message.ljust(message_len)
254 fd1f1d96 Stavros Sachtouris
        except NameError:
255 852a22e7 Stavros Sachtouris
            pass
256 852a22e7 Stavros Sachtouris
        self.bar.suffix = '%(percent)d%% - %(eta)ds'
257 fd1f1d96 Stavros Sachtouris
258 852a22e7 Stavros Sachtouris
        def progress_gen(n):
259 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
260 852a22e7 Stavros Sachtouris
                yield
261 852a22e7 Stavros Sachtouris
            yield
262 852a22e7 Stavros Sachtouris
        return progress_gen
263 fd1f1d96 Stavros Sachtouris
264 852a22e7 Stavros Sachtouris
    def finish(self):
265 852a22e7 Stavros Sachtouris
        if self.value:
266 852a22e7 Stavros Sachtouris
            return
267 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
268 852a22e7 Stavros Sachtouris
        if mybar:
269 852a22e7 Stavros Sachtouris
            mybar.finish()
270 fd1f1d96 Stavros Sachtouris
271 fd1f1d96 Stavros Sachtouris
272 fd5db045 Stavros Sachtouris
_arguments = dict(config=_config_arg,
273 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
274 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
275 fd5db045 Stavros Sachtouris
    include=FlagArgument('Include protocol headers in the output',
276 fd5db045 Stavros Sachtouris
        ('-i', '--include')),
277 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
278 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
279 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
280 fd5db045 Stavros Sachtouris
    options=CmdLineConfigArgument(_config_arg,
281 fd5db045 Stavros Sachtouris
        'Override a config value',
282 fd5db045 Stavros Sachtouris
        ('-o', '--options'))
283 9bdc89da Stavros Sachtouris
)
284 9bdc89da Stavros Sachtouris
285 fd5db045 Stavros Sachtouris
286 ffaf6783 Stavros Sachtouris
def parse_known_args(parser, arguments=None):
287 c41a86b2 Stavros Sachtouris
    parsed, unparsed = parser.parse_known_args()
288 ffaf6783 Stavros Sachtouris
    for name, arg in arguments.items():
289 0f653327 Stavros Sachtouris
        arg.value = getattr(parsed, name, arg.default)
290 c41a86b2 Stavros Sachtouris
    return parsed, unparsed
291 0d249b3e Stavros Sachtouris
    # ['"%s"' % s if ' ' in s else s for s in unparsed]
292 af6de846 Stavros Sachtouris
293 af6de846 Stavros Sachtouris
294 af6de846 Stavros Sachtouris
def init_parser(exe, arguments):
295 af6de846 Stavros Sachtouris
    parser = ArgumentParser(add_help=False)
296 af6de846 Stavros Sachtouris
    parser.prog = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe
297 af6de846 Stavros Sachtouris
    update_arguments(parser, arguments)
298 af6de846 Stavros Sachtouris
    return parser
299 af6de846 Stavros Sachtouris
300 af6de846 Stavros Sachtouris
301 af6de846 Stavros Sachtouris
def update_arguments(parser, arguments):
302 af6de846 Stavros Sachtouris
    for name, argument in arguments.items():
303 af6de846 Stavros Sachtouris
        try:
304 af6de846 Stavros Sachtouris
            argument.update_parser(parser, name)
305 af6de846 Stavros Sachtouris
        except ArgumentError:
306 af6de846 Stavros Sachtouris
            pass