Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument / __init__.py @ 534e7bbb

History | View | Annotate | Download (15.3 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2012-2013 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 b696ed2c Stavros Sachtouris
from kamaki.cli.errors import CLISyntaxError, raiseCLIError
36 e0f40c94 Stavros Sachtouris
from kamaki.cli.utils import split_input
37 7637d600 Stavros Sachtouris
38 04d01cd4 Stavros Sachtouris
from datetime import datetime as dtm
39 ea4a21b8 Stavros Sachtouris
from time import mktime
40 db8d1766 Stavros Sachtouris
41 7637d600 Stavros Sachtouris
from logging import getLogger
42 af6de846 Stavros Sachtouris
from argparse import ArgumentParser, ArgumentError
43 2703cceb Stavros Sachtouris
from argparse import RawDescriptionHelpFormatter
44 67083ca0 Stavros Sachtouris
from progress.bar import ShadyBar as KamakiProgressBar
45 fd1f1d96 Stavros Sachtouris
46 7637d600 Stavros Sachtouris
log = getLogger(__name__)
47 db8d1766 Stavros Sachtouris
48 fd5db045 Stavros Sachtouris
49 dfee2caf Stavros Sachtouris
class Argument(object):
50 edb7fc1a Stavros Sachtouris
    """An argument that can be parsed from command line or otherwise.
51 5286e2c3 Stavros Sachtouris
    This is the top-level Argument class. It is suggested to extent this
52 edb7fc1a Stavros Sachtouris
    class into more specific argument types.
53 edb7fc1a Stavros Sachtouris
    """
54 dfee2caf Stavros Sachtouris
55 dfee2caf Stavros Sachtouris
    def __init__(self, arity, help=None, parsed_name=None, default=None):
56 606fe15f Stavros Sachtouris
        self.arity = int(arity)
57 5286e2c3 Stavros Sachtouris
        self.help = '%s' % help or ''
58 dfee2caf Stavros Sachtouris
59 f17d6cb5 Stavros Sachtouris
        assert parsed_name, 'No parsed name for argument %s' % self
60 f17d6cb5 Stavros Sachtouris
        self.parsed_name = list(parsed_name) if isinstance(
61 f17d6cb5 Stavros Sachtouris
            parsed_name, list) or isinstance(parsed_name, tuple) else (
62 f17d6cb5 Stavros Sachtouris
                '%s' % parsed_name).split()
63 f17d6cb5 Stavros Sachtouris
        for name in self.parsed_name:
64 f17d6cb5 Stavros Sachtouris
            assert name.count(' ') == 0, '%s: Invalid parse name "%s"' % (
65 f17d6cb5 Stavros Sachtouris
                self, name)
66 f17d6cb5 Stavros Sachtouris
            msg = '%s: Invalid parse name "%s" should start with a "-"' % (
67 f17d6cb5 Stavros Sachtouris
                    self, name)
68 f17d6cb5 Stavros Sachtouris
            assert name.startswith('-'), msg
69 f17d6cb5 Stavros Sachtouris
70 f52b39db Stavros Sachtouris
        self.default = default if (default or self.arity) else False
71 dfee2caf Stavros Sachtouris
72 fd5db045 Stavros Sachtouris
    @property
73 dfee2caf Stavros Sachtouris
    def value(self):
74 dfee2caf Stavros Sachtouris
        return getattr(self, '_value', self.default)
75 fd5db045 Stavros Sachtouris
76 dfee2caf Stavros Sachtouris
    @value.setter
77 dfee2caf Stavros Sachtouris
    def value(self, newvalue):
78 dfee2caf Stavros Sachtouris
        self._value = newvalue
79 dfee2caf Stavros Sachtouris
80 dfee2caf Stavros Sachtouris
    def update_parser(self, parser, name):
81 edb7fc1a Stavros Sachtouris
        """Update argument parser with self info"""
82 f17d6cb5 Stavros Sachtouris
        action = 'append' if self.arity < 0 else (
83 f17d6cb5 Stavros Sachtouris
            'store' if self.arity else 'store_true')
84 de73876b Stavros Sachtouris
        parser.add_argument(
85 de73876b Stavros Sachtouris
            *self.parsed_name,
86 f17d6cb5 Stavros Sachtouris
            dest=name, action=action, default=self.default, help=self.help)
87 dfee2caf Stavros Sachtouris
88 fd5db045 Stavros Sachtouris
89 9bdc89da Stavros Sachtouris
class ConfigArgument(Argument):
90 439926dd Stavros Sachtouris
    """Manage a kamaki configuration (file)"""
91 edb7fc1a Stavros Sachtouris
92 a7aacf12 Stavros Sachtouris
    def __init__(self, help, parsed_name=('-c', '--config')):
93 a7aacf12 Stavros Sachtouris
        super(ConfigArgument, self).__init__(1, help, parsed_name, None)
94 a7aacf12 Stavros Sachtouris
        self.file_path = None
95 e9a92550 Stavros Sachtouris
96 fd5db045 Stavros Sachtouris
    @property
97 c41a86b2 Stavros Sachtouris
    def value(self):
98 a7aacf12 Stavros Sachtouris
        return super(ConfigArgument, self).value
99 fd5db045 Stavros Sachtouris
100 c41a86b2 Stavros Sachtouris
    @value.setter
101 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
102 e9a92550 Stavros Sachtouris
        if config_file:
103 e9a92550 Stavros Sachtouris
            self._value = Config(config_file)
104 a7aacf12 Stavros Sachtouris
            self.file_path = config_file
105 a7aacf12 Stavros Sachtouris
        elif self.file_path:
106 a7aacf12 Stavros Sachtouris
            self._value = Config(self.file_path)
107 e9a92550 Stavros Sachtouris
        else:
108 e9a92550 Stavros Sachtouris
            self._value = Config()
109 9bdc89da Stavros Sachtouris
110 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
111 439926dd Stavros Sachtouris
        """Get a configuration setting from the Config object"""
112 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
113 c41a86b2 Stavros Sachtouris
114 a7aacf12 Stavros Sachtouris
    @property
115 a7aacf12 Stavros Sachtouris
    def groups(self):
116 362adf50 Stavros Sachtouris
        suffix = '_cli'
117 362adf50 Stavros Sachtouris
        slen = len(suffix)
118 362adf50 Stavros Sachtouris
        return [term[:-slen] for term in self.value.keys('global') if (
119 362adf50 Stavros Sachtouris
            term.endswith(suffix))]
120 f724cd35 Stavros Sachtouris
121 a7aacf12 Stavros Sachtouris
    @property
122 a7aacf12 Stavros Sachtouris
    def cli_specs(self):
123 362adf50 Stavros Sachtouris
        suffix = '_cli'
124 362adf50 Stavros Sachtouris
        slen = len(suffix)
125 362adf50 Stavros Sachtouris
        return [(k[:-slen], v) for k, v in self.value.items('global') if (
126 362adf50 Stavros Sachtouris
            k.endswith(suffix))]
127 362adf50 Stavros Sachtouris
128 362adf50 Stavros Sachtouris
    def get_global(self, option):
129 534e7bbb Stavros Sachtouris
        return self.value.get('global', option)
130 362adf50 Stavros Sachtouris
131 144b3551 Stavros Sachtouris
    def get_cloud(self, cloud, option):
132 144b3551 Stavros Sachtouris
        return self.value.get_cloud(cloud, option)
133 017d37ce Stavros Sachtouris
134 f17d6cb5 Stavros Sachtouris
135 a7aacf12 Stavros Sachtouris
_config_arg = ConfigArgument('Path to config file')
136 017d37ce Stavros Sachtouris
137 fd5db045 Stavros Sachtouris
138 1bd4f765 Stavros Sachtouris
class RuntimeConfigArgument(Argument):
139 edb7fc1a Stavros Sachtouris
    """Set a run-time setting option (not persistent)"""
140 edb7fc1a Stavros Sachtouris
141 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
142 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
143 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
144 c41a86b2 Stavros Sachtouris
145 fd5db045 Stavros Sachtouris
    @property
146 c41a86b2 Stavros Sachtouris
    def value(self):
147 34c480f2 Stavros Sachtouris
        return super(RuntimeConfigArgument, self).value
148 fd5db045 Stavros Sachtouris
149 c41a86b2 Stavros Sachtouris
    @value.setter
150 c41a86b2 Stavros Sachtouris
    def value(self, options):
151 c41a86b2 Stavros Sachtouris
        if options == self.default:
152 c41a86b2 Stavros Sachtouris
            return
153 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
154 a517ff50 Stavros Sachtouris
            options = ['%s' % options]
155 c41a86b2 Stavros Sachtouris
        for option in options:
156 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
157 c41a86b2 Stavros Sachtouris
            if not sep:
158 de73876b Stavros Sachtouris
                raiseCLIError(
159 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument Syntax Error '),
160 24ff0a35 Stavros Sachtouris
                    details=[
161 24ff0a35 Stavros Sachtouris
                        '%s is missing a "="',
162 24ff0a35 Stavros Sachtouris
                        ' (usage: -o section.key=val)' % option])
163 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
164 0a0b9fb6 Stavros Sachtouris
        if not sep:
165 0a0b9fb6 Stavros Sachtouris
            key = section
166 0a0b9fb6 Stavros Sachtouris
            section = 'global'
167 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
168 0a0b9fb6 Stavros Sachtouris
            section.strip(),
169 fd5db045 Stavros Sachtouris
            key.strip(),
170 fd5db045 Stavros Sachtouris
            val.strip())
171 fd5db045 Stavros Sachtouris
172 c41a86b2 Stavros Sachtouris
173 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
174 edb7fc1a Stavros Sachtouris
    """
175 439926dd Stavros Sachtouris
    :value: true if set, false otherwise
176 edb7fc1a Stavros Sachtouris
    """
177 edb7fc1a Stavros Sachtouris
178 40a9c357 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=False):
179 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
180 c41a86b2 Stavros Sachtouris
181 fd5db045 Stavros Sachtouris
182 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
183 edb7fc1a Stavros Sachtouris
    """
184 edb7fc1a Stavros Sachtouris
    :value type: string
185 edb7fc1a Stavros Sachtouris
    :value returns: given value or default
186 edb7fc1a Stavros Sachtouris
    """
187 edb7fc1a Stavros Sachtouris
188 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
189 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
190 c41a86b2 Stavros Sachtouris
191 fd5db045 Stavros Sachtouris
192 0155548b Stavros Sachtouris
class CommaSeparatedListArgument(ValueArgument):
193 0155548b Stavros Sachtouris
    """
194 0155548b Stavros Sachtouris
    :value type: string
195 0155548b Stavros Sachtouris
    :value returns: list of the comma separated values
196 0155548b Stavros Sachtouris
    """
197 0155548b Stavros Sachtouris
198 0155548b Stavros Sachtouris
    @property
199 0155548b Stavros Sachtouris
    def value(self):
200 0155548b Stavros Sachtouris
        return self._value or list()
201 0155548b Stavros Sachtouris
202 0155548b Stavros Sachtouris
    @value.setter
203 0155548b Stavros Sachtouris
    def value(self, newvalue):
204 0155548b Stavros Sachtouris
        self._value = newvalue.split(',') if newvalue else list()
205 0155548b Stavros Sachtouris
206 0155548b Stavros Sachtouris
207 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
208 edb7fc1a Stavros Sachtouris
209 fd5db045 Stavros Sachtouris
    @property
210 e3d4d442 Stavros Sachtouris
    def value(self):
211 439926dd Stavros Sachtouris
        """integer (type checking)"""
212 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
213 fd5db045 Stavros Sachtouris
214 e3d4d442 Stavros Sachtouris
    @value.setter
215 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
216 4a25486d Stavros Sachtouris
        if newvalue == self.default:
217 4a25486d Stavros Sachtouris
            self._value = newvalue
218 4a25486d Stavros Sachtouris
            return
219 e3d4d442 Stavros Sachtouris
        try:
220 4a25486d Stavros Sachtouris
            if int(newvalue) == float(newvalue):
221 4a25486d Stavros Sachtouris
                self._value = int(newvalue)
222 4a25486d Stavros Sachtouris
            else:
223 4a25486d Stavros Sachtouris
                raise ValueError('Raise int argument error')
224 e3d4d442 Stavros Sachtouris
        except ValueError:
225 24ff0a35 Stavros Sachtouris
            raiseCLIError(CLISyntaxError(
226 24ff0a35 Stavros Sachtouris
                'IntArgument Error',
227 c8e17a67 Stavros Sachtouris
                details=['Value %s not an int' % newvalue]))
228 fd5db045 Stavros Sachtouris
229 e3d4d442 Stavros Sachtouris
230 04d01cd4 Stavros Sachtouris
class DateArgument(ValueArgument):
231 04d01cd4 Stavros Sachtouris
232 b7ff6e0c Stavros Sachtouris
    DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
233 04d01cd4 Stavros Sachtouris
234 b7ff6e0c Stavros Sachtouris
    INPUT_FORMATS = [DATE_FORMAT, '%d-%m-%Y', '%H:%M:%S %d-%m-%Y']
235 04d01cd4 Stavros Sachtouris
236 04d01cd4 Stavros Sachtouris
    @property
237 ea4a21b8 Stavros Sachtouris
    def timestamp(self):
238 ea4a21b8 Stavros Sachtouris
        v = getattr(self, '_value', self.default)
239 ea4a21b8 Stavros Sachtouris
        return mktime(v.timetuple()) if v else None
240 ea4a21b8 Stavros Sachtouris
241 ea4a21b8 Stavros Sachtouris
    @property
242 ea4a21b8 Stavros Sachtouris
    def formated(self):
243 ea4a21b8 Stavros Sachtouris
        v = getattr(self, '_value', self.default)
244 b7ff6e0c Stavros Sachtouris
        return v.strftime(self.DATE_FORMAT) if v else None
245 ea4a21b8 Stavros Sachtouris
246 ea4a21b8 Stavros Sachtouris
    @property
247 04d01cd4 Stavros Sachtouris
    def value(self):
248 ea4a21b8 Stavros Sachtouris
        return self.timestamp
249 04d01cd4 Stavros Sachtouris
250 04d01cd4 Stavros Sachtouris
    @value.setter
251 04d01cd4 Stavros Sachtouris
    def value(self, newvalue):
252 2af87afc Stavros Sachtouris
        self._value = self.format_date(newvalue) if newvalue else self.default
253 04d01cd4 Stavros Sachtouris
254 04d01cd4 Stavros Sachtouris
    def format_date(self, datestr):
255 04d01cd4 Stavros Sachtouris
        for format in self.INPUT_FORMATS:
256 04d01cd4 Stavros Sachtouris
            try:
257 04d01cd4 Stavros Sachtouris
                t = dtm.strptime(datestr, format)
258 04d01cd4 Stavros Sachtouris
            except ValueError:
259 04d01cd4 Stavros Sachtouris
                continue
260 b7ff6e0c Stavros Sachtouris
            return t  # .strftime(self.DATE_FORMAT)
261 b7ff6e0c Stavros Sachtouris
        raiseCLIError(None, 'Date Argument Error', details=[
262 b7ff6e0c Stavros Sachtouris
            '%s not a valid date' % datestr,
263 b7ff6e0c Stavros Sachtouris
            'Correct formats:\n\t%s' % self.INPUT_FORMATS])
264 04d01cd4 Stavros Sachtouris
265 04d01cd4 Stavros Sachtouris
266 c41a86b2 Stavros Sachtouris
class VersionArgument(FlagArgument):
267 edb7fc1a Stavros Sachtouris
    """A flag argument with that prints current version"""
268 edb7fc1a Stavros Sachtouris
269 fd5db045 Stavros Sachtouris
    @property
270 c41a86b2 Stavros Sachtouris
    def value(self):
271 439926dd Stavros Sachtouris
        """bool"""
272 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
273 fd5db045 Stavros Sachtouris
274 c41a86b2 Stavros Sachtouris
    @value.setter
275 c41a86b2 Stavros Sachtouris
    def value(self, newvalue):
276 c41a86b2 Stavros Sachtouris
        self._value = newvalue
277 f17d6cb5 Stavros Sachtouris
        if newvalue:
278 c41a86b2 Stavros Sachtouris
            import kamaki
279 fd5db045 Stavros Sachtouris
            print('kamaki %s' % kamaki.__version__)
280 fd5db045 Stavros Sachtouris
281 9bdc89da Stavros Sachtouris
282 ca5528f1 Stavros Sachtouris
class RepeatableArgument(Argument):
283 ca5528f1 Stavros Sachtouris
    """A value argument that can be repeated"""
284 ca5528f1 Stavros Sachtouris
285 ca5528f1 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
286 ca5528f1 Stavros Sachtouris
        super(RepeatableArgument, self).__init__(
287 ca5528f1 Stavros Sachtouris
            -1, help, parsed_name, default)
288 ca5528f1 Stavros Sachtouris
289 ca5528f1 Stavros Sachtouris
290 0a0b9fb6 Stavros Sachtouris
class KeyValueArgument(Argument):
291 ca5528f1 Stavros Sachtouris
    """A Key=Value Argument that can be repeated
292 edb7fc1a Stavros Sachtouris

293 439926dd Stavros Sachtouris
    :syntax: --<arg> key1=value1 --<arg> key2=value2 ...
294 edb7fc1a Stavros Sachtouris
    """
295 edb7fc1a Stavros Sachtouris
296 2d1202ee Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
297 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
298 f3e94e06 Stavros Sachtouris
299 fd5db045 Stavros Sachtouris
    @property
300 f3e94e06 Stavros Sachtouris
    def value(self):
301 439926dd Stavros Sachtouris
        """
302 8d427cb9 Stavros Sachtouris
        :returns: (dict) {key1: val1, key2: val2, ...}
303 439926dd Stavros Sachtouris
        """
304 f3e94e06 Stavros Sachtouris
        return super(KeyValueArgument, self).value
305 fd5db045 Stavros Sachtouris
306 fd5db045 Stavros Sachtouris
    @value.setter
307 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
308 8d427cb9 Stavros Sachtouris
        """
309 8d427cb9 Stavros Sachtouris
        :param keyvalue_pairs: (str) ['key1=val1', 'key2=val2', ...]
310 8d427cb9 Stavros Sachtouris
        """
311 2d1202ee Stavros Sachtouris
        self._value = getattr(self, '_value', self.value) or {}
312 f52b39db Stavros Sachtouris
        try:
313 f52b39db Stavros Sachtouris
            for pair in keyvalue_pairs:
314 f52b39db Stavros Sachtouris
                key, sep, val = pair.partition('=')
315 f52b39db Stavros Sachtouris
                assert sep, ' %s misses a "=" (usage: key1=val1 )\n' % (pair)
316 a75393fb Stavros Sachtouris
                self._value[key] = val
317 f52b39db Stavros Sachtouris
        except Exception as e:
318 f52b39db Stavros Sachtouris
            raiseCLIError(e, 'KeyValueArgument Syntax Error')
319 f3e94e06 Stavros Sachtouris
320 fd1f1d96 Stavros Sachtouris
321 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
322 439926dd Stavros Sachtouris
    """Manage a progress bar"""
323 fd1f1d96 Stavros Sachtouris
324 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
325 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
326 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
327 fd1f1d96 Stavros Sachtouris
328 852a22e7 Stavros Sachtouris
    def clone(self):
329 439926dd Stavros Sachtouris
        """Get a modifiable copy of this bar"""
330 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
331 c0fbf04c Stavros Sachtouris
            self.help, self.parsed_name, self.default)
332 852a22e7 Stavros Sachtouris
        newarg._value = self._value
333 852a22e7 Stavros Sachtouris
        return newarg
334 852a22e7 Stavros Sachtouris
335 8547cd19 Stavros Sachtouris
    def get_generator(
336 8547cd19 Stavros Sachtouris
            self, message, message_len=25, countdown=False, timeout=100):
337 439926dd Stavros Sachtouris
        """Get a generator to handle progress of the bar (gen.next())"""
338 fd1f1d96 Stavros Sachtouris
        if self.value:
339 fd1f1d96 Stavros Sachtouris
            return None
340 fd1f1d96 Stavros Sachtouris
        try:
341 1d329d27 Stavros Sachtouris
            self.bar = KamakiProgressBar()
342 fd1f1d96 Stavros Sachtouris
        except NameError:
343 329753ae Stavros Sachtouris
            self.value = None
344 329753ae Stavros Sachtouris
            return self.value
345 8547cd19 Stavros Sachtouris
        if countdown:
346 e9c73313 Stavros Sachtouris
            bar_phases = list(self.bar.phases)
347 8547cd19 Stavros Sachtouris
            self.bar.empty_fill, bar_phases[0] = bar_phases[-1], ''
348 8547cd19 Stavros Sachtouris
            bar_phases.reverse()
349 e9c73313 Stavros Sachtouris
            self.bar.phases = bar_phases
350 8547cd19 Stavros Sachtouris
            self.bar.bar_prefix = ' '
351 e9c73313 Stavros Sachtouris
            self.bar.bar_suffix = ' '
352 8547cd19 Stavros Sachtouris
            self.bar.max = timeout or 100
353 8547cd19 Stavros Sachtouris
            self.bar.suffix = '%(remaining)ds to timeout'
354 e9c73313 Stavros Sachtouris
        else:
355 e9c73313 Stavros Sachtouris
            self.bar.suffix = '%(percent)d%% - %(eta)ds'
356 8547cd19 Stavros Sachtouris
        self.bar.eta = timeout or 100
357 329753ae Stavros Sachtouris
        self.bar.message = message.ljust(message_len)
358 a10f5561 Stavros Sachtouris
        self.bar.start()
359 fd1f1d96 Stavros Sachtouris
360 852a22e7 Stavros Sachtouris
        def progress_gen(n):
361 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
362 852a22e7 Stavros Sachtouris
                yield
363 852a22e7 Stavros Sachtouris
            yield
364 852a22e7 Stavros Sachtouris
        return progress_gen
365 fd1f1d96 Stavros Sachtouris
366 852a22e7 Stavros Sachtouris
    def finish(self):
367 439926dd Stavros Sachtouris
        """Stop progress bar, return terminal cursor to user"""
368 852a22e7 Stavros Sachtouris
        if self.value:
369 852a22e7 Stavros Sachtouris
            return
370 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
371 852a22e7 Stavros Sachtouris
        if mybar:
372 852a22e7 Stavros Sachtouris
            mybar.finish()
373 fd1f1d96 Stavros Sachtouris
374 fd1f1d96 Stavros Sachtouris
375 de73876b Stavros Sachtouris
_arguments = dict(
376 de73876b Stavros Sachtouris
    config=_config_arg,
377 144b3551 Stavros Sachtouris
    cloud=ValueArgument('Chose a cloud to connect to', ('--cloud')),
378 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
379 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
380 f6822a26 Stavros Sachtouris
    #include=FlagArgument(
381 f6822a26 Stavros Sachtouris
    #    'Include raw connection data in the output', ('-i', '--include')),
382 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
383 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
384 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
385 1bd4f765 Stavros Sachtouris
    options=RuntimeConfigArgument(
386 cb4a5d9c Stavros Sachtouris
        _config_arg, 'Override a config value', ('-o', '--options'))
387 9bdc89da Stavros Sachtouris
)
388 cb4a5d9c Stavros Sachtouris
389 cb4a5d9c Stavros Sachtouris
390 cb4a5d9c Stavros Sachtouris
#  Initial command line interface arguments
391 439926dd Stavros Sachtouris
392 439926dd Stavros Sachtouris
393 7c2247a0 Stavros Sachtouris
class ArgumentParseManager(object):
394 e0da0f90 Stavros Sachtouris
    """Manage (initialize and update) an ArgumentParser object"""
395 e0da0f90 Stavros Sachtouris
396 e0da0f90 Stavros Sachtouris
    def __init__(self, exe, arguments=None):
397 e0da0f90 Stavros Sachtouris
        """
398 e0da0f90 Stavros Sachtouris
        :param exe: (str) the basic command (e.g. 'kamaki')
399 e0da0f90 Stavros Sachtouris

400 e0da0f90 Stavros Sachtouris
        :param arguments: (dict) if given, overrides the global _argument as
401 e0da0f90 Stavros Sachtouris
            the parsers arguments specification
402 e0da0f90 Stavros Sachtouris
        """
403 de73876b Stavros Sachtouris
        self.parser = ArgumentParser(
404 320aac17 Stavros Sachtouris
            add_help=False, formatter_class=RawDescriptionHelpFormatter)
405 b3dd8f4b Stavros Sachtouris
        self.syntax = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe
406 e0da0f90 Stavros Sachtouris
        if arguments:
407 e0da0f90 Stavros Sachtouris
            self.arguments = arguments
408 e0da0f90 Stavros Sachtouris
        else:
409 e0da0f90 Stavros Sachtouris
            global _arguments
410 e0da0f90 Stavros Sachtouris
            self.arguments = _arguments
411 631b7c35 Stavros Sachtouris
        self._parser_modified, self._parsed, self._unparsed = False, None, None
412 7c2247a0 Stavros Sachtouris
        self.parse()
413 e0da0f90 Stavros Sachtouris
414 e0da0f90 Stavros Sachtouris
    @property
415 e0da0f90 Stavros Sachtouris
    def syntax(self):
416 b3dd8f4b Stavros Sachtouris
        """The command syntax (useful for help messages, descriptions, etc)"""
417 e0da0f90 Stavros Sachtouris
        return self.parser.prog
418 e0da0f90 Stavros Sachtouris
419 e0da0f90 Stavros Sachtouris
    @syntax.setter
420 e0da0f90 Stavros Sachtouris
    def syntax(self, new_syntax):
421 e0da0f90 Stavros Sachtouris
        self.parser.prog = new_syntax
422 e0da0f90 Stavros Sachtouris
423 b3dd8f4b Stavros Sachtouris
    @property
424 b3dd8f4b Stavros Sachtouris
    def arguments(self):
425 631b7c35 Stavros Sachtouris
        """:returns: (dict) arguments the parser should be aware of"""
426 b3dd8f4b Stavros Sachtouris
        return self._arguments
427 b3dd8f4b Stavros Sachtouris
428 b3dd8f4b Stavros Sachtouris
    @arguments.setter
429 b3dd8f4b Stavros Sachtouris
    def arguments(self, new_arguments):
430 631b7c35 Stavros Sachtouris
        assert isinstance(new_arguments, dict), 'Arguments must be in a dict'
431 b3dd8f4b Stavros Sachtouris
        self._arguments = new_arguments
432 b3dd8f4b Stavros Sachtouris
        self.update_parser()
433 b3dd8f4b Stavros Sachtouris
434 7c2247a0 Stavros Sachtouris
    @property
435 b3dd8f4b Stavros Sachtouris
    def parsed(self):
436 7c2247a0 Stavros Sachtouris
        """(Namespace) parser-matched terms"""
437 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
438 b3dd8f4b Stavros Sachtouris
            self.parse()
439 b3dd8f4b Stavros Sachtouris
        return self._parsed
440 b3dd8f4b Stavros Sachtouris
441 b3dd8f4b Stavros Sachtouris
    @property
442 b3dd8f4b Stavros Sachtouris
    def unparsed(self):
443 b3dd8f4b Stavros Sachtouris
        """(list) parser-unmatched terms"""
444 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
445 b3dd8f4b Stavros Sachtouris
            self.parse()
446 b3dd8f4b Stavros Sachtouris
        return self._unparsed
447 b3dd8f4b Stavros Sachtouris
448 e0da0f90 Stavros Sachtouris
    def update_parser(self, arguments=None):
449 e0da0f90 Stavros Sachtouris
        """Load argument specifications to parser
450 e0da0f90 Stavros Sachtouris

451 e0da0f90 Stavros Sachtouris
        :param arguments: if not given, update self.arguments instead
452 e0da0f90 Stavros Sachtouris
        """
453 631b7c35 Stavros Sachtouris
        arguments = arguments or self._arguments
454 e0da0f90 Stavros Sachtouris
455 e0da0f90 Stavros Sachtouris
        for name, arg in arguments.items():
456 e0da0f90 Stavros Sachtouris
            try:
457 e0da0f90 Stavros Sachtouris
                arg.update_parser(self.parser, name)
458 b3dd8f4b Stavros Sachtouris
                self._parser_modified = True
459 e0da0f90 Stavros Sachtouris
            except ArgumentError:
460 e0da0f90 Stavros Sachtouris
                pass
461 e0da0f90 Stavros Sachtouris
462 7c2247a0 Stavros Sachtouris
    def update_arguments(self, new_arguments):
463 7c2247a0 Stavros Sachtouris
        """Add to / update existing arguments
464 7c2247a0 Stavros Sachtouris

465 7c2247a0 Stavros Sachtouris
        :param new_arguments: (dict)
466 7c2247a0 Stavros Sachtouris
        """
467 7c2247a0 Stavros Sachtouris
        if new_arguments:
468 7c2247a0 Stavros Sachtouris
            assert isinstance(new_arguments, dict)
469 7c2247a0 Stavros Sachtouris
            self._arguments.update(new_arguments)
470 7c2247a0 Stavros Sachtouris
            self.update_parser()
471 7c2247a0 Stavros Sachtouris
472 120126f1 Stavros Sachtouris
    def parse(self, new_args=None):
473 320aac17 Stavros Sachtouris
        """Parse user input"""
474 c8e17a67 Stavros Sachtouris
        try:
475 320aac17 Stavros Sachtouris
            pkargs = (new_args,) if new_args else ()
476 320aac17 Stavros Sachtouris
            self._parsed, unparsed = self.parser.parse_known_args(*pkargs)
477 c8e17a67 Stavros Sachtouris
        except SystemExit:
478 c8e17a67 Stavros Sachtouris
            raiseCLIError(CLISyntaxError('Argument Syntax Error'))
479 b3dd8f4b Stavros Sachtouris
        for name, arg in self.arguments.items():
480 b3dd8f4b Stavros Sachtouris
            arg.value = getattr(self._parsed, name, arg.default)
481 b3dd8f4b Stavros Sachtouris
        self._unparsed = []
482 b3dd8f4b Stavros Sachtouris
        for term in unparsed:
483 b3dd8f4b Stavros Sachtouris
            self._unparsed += split_input(' \'%s\' ' % term)
484 b3dd8f4b Stavros Sachtouris
        self._parser_modified = False