Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument.py @ 329753ae

History | View | Annotate | Download (9.7 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 e9a92550 Stavros Sachtouris
    _config_file = None
120 e9a92550 Stavros Sachtouris
121 fd5db045 Stavros Sachtouris
    @property
122 c41a86b2 Stavros Sachtouris
    def value(self):
123 d486baec Stavros Sachtouris
        super(self.__class__, self).value
124 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
125 fd5db045 Stavros Sachtouris
126 c41a86b2 Stavros Sachtouris
    @value.setter
127 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
128 e9a92550 Stavros Sachtouris
        if config_file:
129 e9a92550 Stavros Sachtouris
            self._value = Config(config_file)
130 e9a92550 Stavros Sachtouris
            self._config_file = config_file
131 e9a92550 Stavros Sachtouris
        elif self._config_file:
132 e9a92550 Stavros Sachtouris
            self._value = Config(self._config_file)
133 e9a92550 Stavros Sachtouris
        else:
134 e9a92550 Stavros Sachtouris
            self._value = Config()
135 9bdc89da Stavros Sachtouris
136 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
137 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
138 c41a86b2 Stavros Sachtouris
139 c41a86b2 Stavros Sachtouris
    def get_groups(self):
140 c41a86b2 Stavros Sachtouris
        return self.value.apis()
141 017d37ce Stavros Sachtouris
142 017d37ce Stavros Sachtouris
_config_arg = ConfigArgument(1, 'Path to configuration file', '--config')
143 017d37ce Stavros Sachtouris
144 fd5db045 Stavros Sachtouris
145 9bdc89da Stavros Sachtouris
class CmdLineConfigArgument(Argument):
146 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
147 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
148 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
149 c41a86b2 Stavros Sachtouris
150 fd5db045 Stavros Sachtouris
    @property
151 c41a86b2 Stavros Sachtouris
    def value(self):
152 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
153 fd5db045 Stavros Sachtouris
154 c41a86b2 Stavros Sachtouris
    @value.setter
155 c41a86b2 Stavros Sachtouris
    def value(self, options):
156 c41a86b2 Stavros Sachtouris
        if options == self.default:
157 c41a86b2 Stavros Sachtouris
            return
158 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
159 fd5db045 Stavros Sachtouris
            options = [unicode(options)]
160 c41a86b2 Stavros Sachtouris
        for option in options:
161 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
162 c41a86b2 Stavros Sachtouris
            if not sep:
163 0a0b9fb6 Stavros Sachtouris
                raise CLISyntaxError('Argument Syntax Error ',
164 71882bca Stavros Sachtouris
                    details='%s is missing a "=" (usage: -o section.key=val)'\
165 0a0b9fb6 Stavros Sachtouris
                        % option)
166 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
167 0a0b9fb6 Stavros Sachtouris
        if not sep:
168 0a0b9fb6 Stavros Sachtouris
            key = section
169 0a0b9fb6 Stavros Sachtouris
            section = 'global'
170 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
171 0a0b9fb6 Stavros Sachtouris
            section.strip(),
172 fd5db045 Stavros Sachtouris
            key.strip(),
173 fd5db045 Stavros Sachtouris
            val.strip())
174 fd5db045 Stavros Sachtouris
175 c41a86b2 Stavros Sachtouris
176 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
177 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
178 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
179 c41a86b2 Stavros Sachtouris
180 fd5db045 Stavros Sachtouris
181 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
182 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
183 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
184 c41a86b2 Stavros Sachtouris
185 fd5db045 Stavros Sachtouris
186 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
187 fd5db045 Stavros Sachtouris
    @property
188 e3d4d442 Stavros Sachtouris
    def value(self):
189 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
190 fd5db045 Stavros Sachtouris
191 e3d4d442 Stavros Sachtouris
    @value.setter
192 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
193 e3d4d442 Stavros Sachtouris
        if newvalue == self.default:
194 e3d4d442 Stavros Sachtouris
            self._value = self.default
195 e3d4d442 Stavros Sachtouris
            return
196 e3d4d442 Stavros Sachtouris
        try:
197 e3d4d442 Stavros Sachtouris
            self._value = int(newvalue)
198 e3d4d442 Stavros Sachtouris
        except ValueError:
199 fd5db045 Stavros Sachtouris
            raise CLISyntaxError('IntArgument Error',
200 fd5db045 Stavros Sachtouris
                details='Value %s not an int' % newvalue)
201 fd5db045 Stavros Sachtouris
202 e3d4d442 Stavros Sachtouris
203 c41a86b2 Stavros Sachtouris
class VersionArgument(FlagArgument):
204 fd5db045 Stavros Sachtouris
    @property
205 c41a86b2 Stavros Sachtouris
    def value(self):
206 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
207 fd5db045 Stavros Sachtouris
208 c41a86b2 Stavros Sachtouris
    @value.setter
209 c41a86b2 Stavros Sachtouris
    def value(self, newvalue):
210 c41a86b2 Stavros Sachtouris
        self._value = newvalue
211 c41a86b2 Stavros Sachtouris
        self.main()
212 c41a86b2 Stavros Sachtouris
213 c41a86b2 Stavros Sachtouris
    def main(self):
214 c41a86b2 Stavros Sachtouris
        if self.value:
215 c41a86b2 Stavros Sachtouris
            import kamaki
216 fd5db045 Stavros Sachtouris
            print('kamaki %s' % kamaki.__version__)
217 fd5db045 Stavros Sachtouris
218 9bdc89da Stavros Sachtouris
219 0a0b9fb6 Stavros Sachtouris
class KeyValueArgument(Argument):
220 0a0b9fb6 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
221 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
222 f3e94e06 Stavros Sachtouris
223 fd5db045 Stavros Sachtouris
    @property
224 f3e94e06 Stavros Sachtouris
    def value(self):
225 f3e94e06 Stavros Sachtouris
        return super(KeyValueArgument, self).value
226 fd5db045 Stavros Sachtouris
227 fd5db045 Stavros Sachtouris
    @value.setter
228 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
229 0a0b9fb6 Stavros Sachtouris
        self._value = {}
230 f3e94e06 Stavros Sachtouris
        for pair in keyvalue_pairs:
231 fd5db045 Stavros Sachtouris
            key, sep, val = pair.partition('=')
232 f3e94e06 Stavros Sachtouris
            if not sep:
233 0a0b9fb6 Stavros Sachtouris
                raise CLISyntaxError('Argument syntax error ',
234 0a0b9fb6 Stavros Sachtouris
                    details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
235 23803b28 Stavros Sachtouris
            self._value[key.strip()] = val.strip()
236 f3e94e06 Stavros Sachtouris
237 fd1f1d96 Stavros Sachtouris
238 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
239 fd1f1d96 Stavros Sachtouris
240 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
241 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
242 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
243 fd1f1d96 Stavros Sachtouris
        try:
244 852a22e7 Stavros Sachtouris
            IncrementalBar
245 fd1f1d96 Stavros Sachtouris
        except NameError:
246 fd1f1d96 Stavros Sachtouris
            print('Waring: no progress bar functionality')
247 fd1f1d96 Stavros Sachtouris
248 852a22e7 Stavros Sachtouris
    def clone(self):
249 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
250 852a22e7 Stavros Sachtouris
            self.help,
251 852a22e7 Stavros Sachtouris
            self.parsed_name,
252 852a22e7 Stavros Sachtouris
            self.default)
253 852a22e7 Stavros Sachtouris
        newarg._value = self._value
254 852a22e7 Stavros Sachtouris
        return newarg
255 852a22e7 Stavros Sachtouris
256 fd1f1d96 Stavros Sachtouris
    def get_generator(self, message, message_len=25):
257 fd1f1d96 Stavros Sachtouris
        if self.value:
258 fd1f1d96 Stavros Sachtouris
            return None
259 fd1f1d96 Stavros Sachtouris
        try:
260 329753ae Stavros Sachtouris
            self.bar = IncrementalBar()
261 fd1f1d96 Stavros Sachtouris
        except NameError:
262 329753ae Stavros Sachtouris
            self.value = None
263 329753ae Stavros Sachtouris
            return self.value
264 329753ae Stavros Sachtouris
        self.bar.message = message.ljust(message_len)
265 852a22e7 Stavros Sachtouris
        self.bar.suffix = '%(percent)d%% - %(eta)ds'
266 fd1f1d96 Stavros Sachtouris
267 852a22e7 Stavros Sachtouris
        def progress_gen(n):
268 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
269 852a22e7 Stavros Sachtouris
                yield
270 852a22e7 Stavros Sachtouris
            yield
271 852a22e7 Stavros Sachtouris
        return progress_gen
272 fd1f1d96 Stavros Sachtouris
273 852a22e7 Stavros Sachtouris
    def finish(self):
274 852a22e7 Stavros Sachtouris
        if self.value:
275 852a22e7 Stavros Sachtouris
            return
276 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
277 852a22e7 Stavros Sachtouris
        if mybar:
278 852a22e7 Stavros Sachtouris
            mybar.finish()
279 fd1f1d96 Stavros Sachtouris
280 fd1f1d96 Stavros Sachtouris
281 fd5db045 Stavros Sachtouris
_arguments = dict(config=_config_arg,
282 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
283 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
284 fd5db045 Stavros Sachtouris
    include=FlagArgument('Include protocol headers in the output',
285 fd5db045 Stavros Sachtouris
        ('-i', '--include')),
286 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
287 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
288 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
289 fd5db045 Stavros Sachtouris
    options=CmdLineConfigArgument(_config_arg,
290 fd5db045 Stavros Sachtouris
        'Override a config value',
291 fd5db045 Stavros Sachtouris
        ('-o', '--options'))
292 9bdc89da Stavros Sachtouris
)
293 9bdc89da Stavros Sachtouris
294 fd5db045 Stavros Sachtouris
295 ffaf6783 Stavros Sachtouris
def parse_known_args(parser, arguments=None):
296 c41a86b2 Stavros Sachtouris
    parsed, unparsed = parser.parse_known_args()
297 ffaf6783 Stavros Sachtouris
    for name, arg in arguments.items():
298 0f653327 Stavros Sachtouris
        arg.value = getattr(parsed, name, arg.default)
299 1c1a0941 Stavros Sachtouris
    unparsed = ['"%s"' % s if ' ' in s else s for s in unparsed]
300 c41a86b2 Stavros Sachtouris
    return parsed, unparsed
301 af6de846 Stavros Sachtouris
302 af6de846 Stavros Sachtouris
303 af6de846 Stavros Sachtouris
def init_parser(exe, arguments):
304 af6de846 Stavros Sachtouris
    parser = ArgumentParser(add_help=False)
305 af6de846 Stavros Sachtouris
    parser.prog = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe
306 af6de846 Stavros Sachtouris
    update_arguments(parser, arguments)
307 af6de846 Stavros Sachtouris
    return parser
308 af6de846 Stavros Sachtouris
309 af6de846 Stavros Sachtouris
310 af6de846 Stavros Sachtouris
def update_arguments(parser, arguments):
311 af6de846 Stavros Sachtouris
    for name, argument in arguments.items():
312 af6de846 Stavros Sachtouris
        try:
313 af6de846 Stavros Sachtouris
            argument.update_parser(parser, name)
314 af6de846 Stavros Sachtouris
        except ArgumentError:
315 af6de846 Stavros Sachtouris
            pass