Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument / __init__.py @ 1bd4f765

History | View | Annotate | Download (15 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 dfee2caf Stavros Sachtouris
45 fd1f1d96 Stavros Sachtouris
try:
46 365280ca Stavros Sachtouris
    from progress.bar import ShadyBar as KamakiProgressBar
47 fd1f1d96 Stavros Sachtouris
except ImportError:
48 ca092af4 Stavros Sachtouris
    try:
49 ca092af4 Stavros Sachtouris
        from progress.bar import Bar as KamakiProgressBar
50 ca092af4 Stavros Sachtouris
    except ImportError:
51 ca092af4 Stavros Sachtouris
        pass
52 fd1f1d96 Stavros Sachtouris
    # progress not installed - pls, pip install progress
53 fd1f1d96 Stavros Sachtouris
    pass
54 fd1f1d96 Stavros Sachtouris
55 7637d600 Stavros Sachtouris
log = getLogger(__name__)
56 db8d1766 Stavros Sachtouris
57 fd5db045 Stavros Sachtouris
58 dfee2caf Stavros Sachtouris
class Argument(object):
59 edb7fc1a Stavros Sachtouris
    """An argument that can be parsed from command line or otherwise.
60 5286e2c3 Stavros Sachtouris
    This is the top-level Argument class. It is suggested to extent this
61 edb7fc1a Stavros Sachtouris
    class into more specific argument types.
62 edb7fc1a Stavros Sachtouris
    """
63 dfee2caf Stavros Sachtouris
64 dfee2caf Stavros Sachtouris
    def __init__(self, arity, help=None, parsed_name=None, default=None):
65 606fe15f Stavros Sachtouris
        self.arity = int(arity)
66 5286e2c3 Stavros Sachtouris
        self.help = '%s' % help or ''
67 dfee2caf Stavros Sachtouris
68 f17d6cb5 Stavros Sachtouris
        assert parsed_name, 'No parsed name for argument %s' % self
69 f17d6cb5 Stavros Sachtouris
        self.parsed_name = list(parsed_name) if isinstance(
70 f17d6cb5 Stavros Sachtouris
            parsed_name, list) or isinstance(parsed_name, tuple) else (
71 f17d6cb5 Stavros Sachtouris
                '%s' % parsed_name).split()
72 f17d6cb5 Stavros Sachtouris
        for name in self.parsed_name:
73 f17d6cb5 Stavros Sachtouris
            assert name.count(' ') == 0, '%s: Invalid parse name "%s"' % (
74 f17d6cb5 Stavros Sachtouris
                self, name)
75 f17d6cb5 Stavros Sachtouris
            msg = '%s: Invalid parse name "%s" should start with a "-"' % (
76 f17d6cb5 Stavros Sachtouris
                    self, name)
77 f17d6cb5 Stavros Sachtouris
            assert name.startswith('-'), msg
78 f17d6cb5 Stavros Sachtouris
79 f17d6cb5 Stavros Sachtouris
        self.default = default or (None if self.arity else False)
80 dfee2caf Stavros Sachtouris
81 fd5db045 Stavros Sachtouris
    @property
82 dfee2caf Stavros Sachtouris
    def value(self):
83 dfee2caf Stavros Sachtouris
        return getattr(self, '_value', self.default)
84 fd5db045 Stavros Sachtouris
85 dfee2caf Stavros Sachtouris
    @value.setter
86 dfee2caf Stavros Sachtouris
    def value(self, newvalue):
87 dfee2caf Stavros Sachtouris
        self._value = newvalue
88 dfee2caf Stavros Sachtouris
89 dfee2caf Stavros Sachtouris
    def update_parser(self, parser, name):
90 edb7fc1a Stavros Sachtouris
        """Update argument parser with self info"""
91 f17d6cb5 Stavros Sachtouris
        action = 'append' if self.arity < 0 else (
92 f17d6cb5 Stavros Sachtouris
            'store' if self.arity else 'store_true')
93 de73876b Stavros Sachtouris
        parser.add_argument(
94 de73876b Stavros Sachtouris
            *self.parsed_name,
95 f17d6cb5 Stavros Sachtouris
            dest=name, action=action, default=self.default, help=self.help)
96 dfee2caf Stavros Sachtouris
97 fd5db045 Stavros Sachtouris
98 9bdc89da Stavros Sachtouris
class ConfigArgument(Argument):
99 439926dd Stavros Sachtouris
    """Manage a kamaki configuration (file)"""
100 edb7fc1a Stavros Sachtouris
101 a7aacf12 Stavros Sachtouris
    def __init__(self, help, parsed_name=('-c', '--config')):
102 a7aacf12 Stavros Sachtouris
        super(ConfigArgument, self).__init__(1, help, parsed_name, None)
103 a7aacf12 Stavros Sachtouris
        self.file_path = None
104 e9a92550 Stavros Sachtouris
105 fd5db045 Stavros Sachtouris
    @property
106 c41a86b2 Stavros Sachtouris
    def value(self):
107 a7aacf12 Stavros Sachtouris
        return super(ConfigArgument, self).value
108 fd5db045 Stavros Sachtouris
109 c41a86b2 Stavros Sachtouris
    @value.setter
110 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
111 e9a92550 Stavros Sachtouris
        if config_file:
112 e9a92550 Stavros Sachtouris
            self._value = Config(config_file)
113 a7aacf12 Stavros Sachtouris
            self.file_path = config_file
114 a7aacf12 Stavros Sachtouris
        elif self.file_path:
115 a7aacf12 Stavros Sachtouris
            self._value = Config(self.file_path)
116 e9a92550 Stavros Sachtouris
        else:
117 e9a92550 Stavros Sachtouris
            self._value = Config()
118 9bdc89da Stavros Sachtouris
119 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
120 439926dd Stavros Sachtouris
        """Get a configuration setting from the Config object"""
121 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
122 c41a86b2 Stavros Sachtouris
123 a7aacf12 Stavros Sachtouris
    @property
124 a7aacf12 Stavros Sachtouris
    def groups(self):
125 362adf50 Stavros Sachtouris
        suffix = '_cli'
126 362adf50 Stavros Sachtouris
        slen = len(suffix)
127 362adf50 Stavros Sachtouris
        return [term[:-slen] for term in self.value.keys('global') if (
128 362adf50 Stavros Sachtouris
            term.endswith(suffix))]
129 f724cd35 Stavros Sachtouris
130 a7aacf12 Stavros Sachtouris
    @property
131 a7aacf12 Stavros Sachtouris
    def cli_specs(self):
132 362adf50 Stavros Sachtouris
        suffix = '_cli'
133 362adf50 Stavros Sachtouris
        slen = len(suffix)
134 362adf50 Stavros Sachtouris
        return [(k[:-slen], v) for k, v in self.value.items('global') if (
135 362adf50 Stavros Sachtouris
            k.endswith(suffix))]
136 362adf50 Stavros Sachtouris
137 362adf50 Stavros Sachtouris
    def get_global(self, option):
138 362adf50 Stavros Sachtouris
        return self.value.get_global(option)
139 362adf50 Stavros Sachtouris
140 144b3551 Stavros Sachtouris
    def get_cloud(self, cloud, option):
141 144b3551 Stavros Sachtouris
        return self.value.get_cloud(cloud, option)
142 017d37ce Stavros Sachtouris
143 f17d6cb5 Stavros Sachtouris
144 a7aacf12 Stavros Sachtouris
_config_arg = ConfigArgument('Path to config file')
145 017d37ce Stavros Sachtouris
146 fd5db045 Stavros Sachtouris
147 1bd4f765 Stavros Sachtouris
class RuntimeConfigArgument(Argument):
148 edb7fc1a Stavros Sachtouris
    """Set a run-time setting option (not persistent)"""
149 edb7fc1a Stavros Sachtouris
150 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
151 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
152 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
153 c41a86b2 Stavros Sachtouris
154 fd5db045 Stavros Sachtouris
    @property
155 c41a86b2 Stavros Sachtouris
    def value(self):
156 439926dd Stavros Sachtouris
        """A key=val option"""
157 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
158 fd5db045 Stavros Sachtouris
159 c41a86b2 Stavros Sachtouris
    @value.setter
160 c41a86b2 Stavros Sachtouris
    def value(self, options):
161 c41a86b2 Stavros Sachtouris
        if options == self.default:
162 c41a86b2 Stavros Sachtouris
            return
163 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
164 a517ff50 Stavros Sachtouris
            options = ['%s' % options]
165 c41a86b2 Stavros Sachtouris
        for option in options:
166 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
167 c41a86b2 Stavros Sachtouris
            if not sep:
168 de73876b Stavros Sachtouris
                raiseCLIError(
169 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument Syntax Error '),
170 24ff0a35 Stavros Sachtouris
                    details=[
171 24ff0a35 Stavros Sachtouris
                        '%s is missing a "="',
172 24ff0a35 Stavros Sachtouris
                        ' (usage: -o section.key=val)' % option])
173 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
174 0a0b9fb6 Stavros Sachtouris
        if not sep:
175 0a0b9fb6 Stavros Sachtouris
            key = section
176 0a0b9fb6 Stavros Sachtouris
            section = 'global'
177 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
178 0a0b9fb6 Stavros Sachtouris
            section.strip(),
179 fd5db045 Stavros Sachtouris
            key.strip(),
180 fd5db045 Stavros Sachtouris
            val.strip())
181 fd5db045 Stavros Sachtouris
182 c41a86b2 Stavros Sachtouris
183 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
184 edb7fc1a Stavros Sachtouris
    """
185 439926dd Stavros Sachtouris
    :value: true if set, false otherwise
186 edb7fc1a Stavros Sachtouris
    """
187 edb7fc1a Stavros Sachtouris
188 40a9c357 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=False):
189 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
190 c41a86b2 Stavros Sachtouris
191 fd5db045 Stavros Sachtouris
192 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
193 edb7fc1a Stavros Sachtouris
    """
194 edb7fc1a Stavros Sachtouris
    :value type: string
195 edb7fc1a Stavros Sachtouris
    :value returns: given value or default
196 edb7fc1a Stavros Sachtouris
    """
197 edb7fc1a Stavros Sachtouris
198 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
199 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
200 c41a86b2 Stavros Sachtouris
201 fd5db045 Stavros Sachtouris
202 0155548b Stavros Sachtouris
class CommaSeparatedListArgument(ValueArgument):
203 0155548b Stavros Sachtouris
    """
204 0155548b Stavros Sachtouris
    :value type: string
205 0155548b Stavros Sachtouris
    :value returns: list of the comma separated values
206 0155548b Stavros Sachtouris
    """
207 0155548b Stavros Sachtouris
208 0155548b Stavros Sachtouris
    @property
209 0155548b Stavros Sachtouris
    def value(self):
210 0155548b Stavros Sachtouris
        return self._value or list()
211 0155548b Stavros Sachtouris
212 0155548b Stavros Sachtouris
    @value.setter
213 0155548b Stavros Sachtouris
    def value(self, newvalue):
214 0155548b Stavros Sachtouris
        self._value = newvalue.split(',') if newvalue else list()
215 0155548b Stavros Sachtouris
216 0155548b Stavros Sachtouris
217 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
218 edb7fc1a Stavros Sachtouris
219 fd5db045 Stavros Sachtouris
    @property
220 e3d4d442 Stavros Sachtouris
    def value(self):
221 439926dd Stavros Sachtouris
        """integer (type checking)"""
222 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
223 fd5db045 Stavros Sachtouris
224 e3d4d442 Stavros Sachtouris
    @value.setter
225 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
226 e3d4d442 Stavros Sachtouris
        if newvalue == self.default:
227 e3d4d442 Stavros Sachtouris
            self._value = self.default
228 e3d4d442 Stavros Sachtouris
            return
229 e3d4d442 Stavros Sachtouris
        try:
230 e3d4d442 Stavros Sachtouris
            self._value = int(newvalue)
231 e3d4d442 Stavros Sachtouris
        except ValueError:
232 24ff0a35 Stavros Sachtouris
            raiseCLIError(CLISyntaxError(
233 24ff0a35 Stavros Sachtouris
                'IntArgument Error',
234 c8e17a67 Stavros Sachtouris
                details=['Value %s not an int' % newvalue]))
235 fd5db045 Stavros Sachtouris
236 e3d4d442 Stavros Sachtouris
237 04d01cd4 Stavros Sachtouris
class DateArgument(ValueArgument):
238 04d01cd4 Stavros Sachtouris
    """
239 04d01cd4 Stavros Sachtouris
    :value type: a string formated in an acceptable date format
240 04d01cd4 Stavros Sachtouris

241 04d01cd4 Stavros Sachtouris
    :value returns: same date in first of DATE_FORMATS
242 04d01cd4 Stavros Sachtouris
    """
243 04d01cd4 Stavros Sachtouris
244 de73876b Stavros Sachtouris
    DATE_FORMATS = [
245 de73876b Stavros Sachtouris
        "%a %b %d %H:%M:%S %Y",
246 04d01cd4 Stavros Sachtouris
        "%A, %d-%b-%y %H:%M:%S GMT",
247 04d01cd4 Stavros Sachtouris
        "%a, %d %b %Y %H:%M:%S GMT"]
248 04d01cd4 Stavros Sachtouris
249 04d01cd4 Stavros Sachtouris
    INPUT_FORMATS = DATE_FORMATS + ["%d-%m-%Y", "%H:%M:%S %d-%m-%Y"]
250 04d01cd4 Stavros Sachtouris
251 04d01cd4 Stavros Sachtouris
    @property
252 ea4a21b8 Stavros Sachtouris
    def timestamp(self):
253 ea4a21b8 Stavros Sachtouris
        v = getattr(self, '_value', self.default)
254 ea4a21b8 Stavros Sachtouris
        return mktime(v.timetuple()) if v else None
255 ea4a21b8 Stavros Sachtouris
256 ea4a21b8 Stavros Sachtouris
    @property
257 ea4a21b8 Stavros Sachtouris
    def formated(self):
258 ea4a21b8 Stavros Sachtouris
        v = getattr(self, '_value', self.default)
259 ea4a21b8 Stavros Sachtouris
        return v.strftime(self.DATE_FORMATS[0]) if v else None
260 ea4a21b8 Stavros Sachtouris
261 ea4a21b8 Stavros Sachtouris
    @property
262 04d01cd4 Stavros Sachtouris
    def value(self):
263 ea4a21b8 Stavros Sachtouris
        return self.timestamp
264 04d01cd4 Stavros Sachtouris
265 04d01cd4 Stavros Sachtouris
    @value.setter
266 04d01cd4 Stavros Sachtouris
    def value(self, newvalue):
267 ea4a21b8 Stavros Sachtouris
        if newvalue:
268 ea4a21b8 Stavros Sachtouris
            self._value = self.format_date(newvalue)
269 04d01cd4 Stavros Sachtouris
270 04d01cd4 Stavros Sachtouris
    def format_date(self, datestr):
271 04d01cd4 Stavros Sachtouris
        for format in self.INPUT_FORMATS:
272 04d01cd4 Stavros Sachtouris
            try:
273 04d01cd4 Stavros Sachtouris
                t = dtm.strptime(datestr, format)
274 04d01cd4 Stavros Sachtouris
            except ValueError:
275 04d01cd4 Stavros Sachtouris
                continue
276 ea4a21b8 Stavros Sachtouris
            return t  # .strftime(self.DATE_FORMATS[0])
277 de73876b Stavros Sachtouris
        raiseCLIError(
278 de73876b Stavros Sachtouris
            None,
279 04d01cd4 Stavros Sachtouris
            'Date Argument Error',
280 24ff0a35 Stavros Sachtouris
            details='%s not a valid date. correct formats:\n\t%s' % (
281 24ff0a35 Stavros Sachtouris
                datestr, self.INPUT_FORMATS))
282 04d01cd4 Stavros Sachtouris
283 04d01cd4 Stavros Sachtouris
284 c41a86b2 Stavros Sachtouris
class VersionArgument(FlagArgument):
285 edb7fc1a Stavros Sachtouris
    """A flag argument with that prints current version"""
286 edb7fc1a Stavros Sachtouris
287 fd5db045 Stavros Sachtouris
    @property
288 c41a86b2 Stavros Sachtouris
    def value(self):
289 439926dd Stavros Sachtouris
        """bool"""
290 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
291 fd5db045 Stavros Sachtouris
292 c41a86b2 Stavros Sachtouris
    @value.setter
293 c41a86b2 Stavros Sachtouris
    def value(self, newvalue):
294 c41a86b2 Stavros Sachtouris
        self._value = newvalue
295 f17d6cb5 Stavros Sachtouris
        if newvalue:
296 c41a86b2 Stavros Sachtouris
            import kamaki
297 fd5db045 Stavros Sachtouris
            print('kamaki %s' % kamaki.__version__)
298 fd5db045 Stavros Sachtouris
299 9bdc89da Stavros Sachtouris
300 0a0b9fb6 Stavros Sachtouris
class KeyValueArgument(Argument):
301 edb7fc1a Stavros Sachtouris
    """A Value Argument that can be repeated
302 edb7fc1a Stavros Sachtouris

303 439926dd Stavros Sachtouris
    :syntax: --<arg> key1=value1 --<arg> key2=value2 ...
304 edb7fc1a Stavros Sachtouris
    """
305 edb7fc1a Stavros Sachtouris
306 0a0b9fb6 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
307 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
308 f3e94e06 Stavros Sachtouris
309 fd5db045 Stavros Sachtouris
    @property
310 f3e94e06 Stavros Sachtouris
    def value(self):
311 439926dd Stavros Sachtouris
        """
312 439926dd Stavros Sachtouris
        :input: key=value
313 439926dd Stavros Sachtouris
        :output: {'key1':'value1', 'key2':'value2', ...}
314 439926dd Stavros Sachtouris
        """
315 f3e94e06 Stavros Sachtouris
        return super(KeyValueArgument, self).value
316 fd5db045 Stavros Sachtouris
317 fd5db045 Stavros Sachtouris
    @value.setter
318 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
319 0a0b9fb6 Stavros Sachtouris
        self._value = {}
320 f3e94e06 Stavros Sachtouris
        for pair in keyvalue_pairs:
321 fd5db045 Stavros Sachtouris
            key, sep, val = pair.partition('=')
322 f3e94e06 Stavros Sachtouris
            if not sep:
323 de73876b Stavros Sachtouris
                raiseCLIError(
324 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument syntax error '),
325 0a0b9fb6 Stavros Sachtouris
                    details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
326 23803b28 Stavros Sachtouris
            self._value[key.strip()] = val.strip()
327 f3e94e06 Stavros Sachtouris
328 fd1f1d96 Stavros Sachtouris
329 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
330 439926dd Stavros Sachtouris
    """Manage a progress bar"""
331 fd1f1d96 Stavros Sachtouris
332 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
333 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
334 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
335 fd1f1d96 Stavros Sachtouris
        try:
336 1d329d27 Stavros Sachtouris
            KamakiProgressBar
337 fd1f1d96 Stavros Sachtouris
        except NameError:
338 7637d600 Stavros Sachtouris
            log.warning('WARNING: no progress bar functionality')
339 fd1f1d96 Stavros Sachtouris
340 852a22e7 Stavros Sachtouris
    def clone(self):
341 439926dd Stavros Sachtouris
        """Get a modifiable copy of this bar"""
342 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
343 852a22e7 Stavros Sachtouris
            self.help,
344 852a22e7 Stavros Sachtouris
            self.parsed_name,
345 852a22e7 Stavros Sachtouris
            self.default)
346 852a22e7 Stavros Sachtouris
        newarg._value = self._value
347 852a22e7 Stavros Sachtouris
        return newarg
348 852a22e7 Stavros Sachtouris
349 fd1f1d96 Stavros Sachtouris
    def get_generator(self, message, message_len=25):
350 439926dd Stavros Sachtouris
        """Get a generator to handle progress of the bar (gen.next())"""
351 fd1f1d96 Stavros Sachtouris
        if self.value:
352 fd1f1d96 Stavros Sachtouris
            return None
353 fd1f1d96 Stavros Sachtouris
        try:
354 1d329d27 Stavros Sachtouris
            self.bar = KamakiProgressBar()
355 fd1f1d96 Stavros Sachtouris
        except NameError:
356 329753ae Stavros Sachtouris
            self.value = None
357 329753ae Stavros Sachtouris
            return self.value
358 329753ae Stavros Sachtouris
        self.bar.message = message.ljust(message_len)
359 852a22e7 Stavros Sachtouris
        self.bar.suffix = '%(percent)d%% - %(eta)ds'
360 a10f5561 Stavros Sachtouris
        self.bar.start()
361 fd1f1d96 Stavros Sachtouris
362 852a22e7 Stavros Sachtouris
        def progress_gen(n):
363 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
364 852a22e7 Stavros Sachtouris
                yield
365 852a22e7 Stavros Sachtouris
            yield
366 852a22e7 Stavros Sachtouris
        return progress_gen
367 fd1f1d96 Stavros Sachtouris
368 852a22e7 Stavros Sachtouris
    def finish(self):
369 439926dd Stavros Sachtouris
        """Stop progress bar, return terminal cursor to user"""
370 852a22e7 Stavros Sachtouris
        if self.value:
371 852a22e7 Stavros Sachtouris
            return
372 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
373 852a22e7 Stavros Sachtouris
        if mybar:
374 852a22e7 Stavros Sachtouris
            mybar.finish()
375 fd1f1d96 Stavros Sachtouris
376 fd1f1d96 Stavros Sachtouris
377 de73876b Stavros Sachtouris
_arguments = dict(
378 de73876b Stavros Sachtouris
    config=_config_arg,
379 144b3551 Stavros Sachtouris
    cloud=ValueArgument('Chose a cloud to connect to', ('--cloud')),
380 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
381 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
382 de73876b Stavros Sachtouris
    include=FlagArgument(
383 cb4a5d9c Stavros Sachtouris
        'Include raw connection data in the output', ('-i', '--include')),
384 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
385 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
386 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
387 1bd4f765 Stavros Sachtouris
    options=RuntimeConfigArgument(
388 cb4a5d9c Stavros Sachtouris
        _config_arg, 'Override a config value', ('-o', '--options'))
389 9bdc89da Stavros Sachtouris
)
390 cb4a5d9c Stavros Sachtouris
391 cb4a5d9c Stavros Sachtouris
392 cb4a5d9c Stavros Sachtouris
#  Initial command line interface arguments
393 439926dd Stavros Sachtouris
394 439926dd Stavros Sachtouris
395 7c2247a0 Stavros Sachtouris
class ArgumentParseManager(object):
396 e0da0f90 Stavros Sachtouris
    """Manage (initialize and update) an ArgumentParser object"""
397 e0da0f90 Stavros Sachtouris
398 5ad77121 Stavros Sachtouris
    parser = None
399 7c2247a0 Stavros Sachtouris
    _arguments = {}
400 b3dd8f4b Stavros Sachtouris
    _parser_modified = False
401 b3dd8f4b Stavros Sachtouris
    _parsed = None
402 b3dd8f4b Stavros Sachtouris
    _unparsed = None
403 e0da0f90 Stavros Sachtouris
404 e0da0f90 Stavros Sachtouris
    def __init__(self, exe, arguments=None):
405 e0da0f90 Stavros Sachtouris
        """
406 e0da0f90 Stavros Sachtouris
        :param exe: (str) the basic command (e.g. 'kamaki')
407 e0da0f90 Stavros Sachtouris

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

459 e0da0f90 Stavros Sachtouris
        :param arguments: if not given, update self.arguments instead
460 e0da0f90 Stavros Sachtouris
        """
461 e0da0f90 Stavros Sachtouris
        if not arguments:
462 b3dd8f4b Stavros Sachtouris
            arguments = self._arguments
463 e0da0f90 Stavros Sachtouris
464 e0da0f90 Stavros Sachtouris
        for name, arg in arguments.items():
465 e0da0f90 Stavros Sachtouris
            try:
466 e0da0f90 Stavros Sachtouris
                arg.update_parser(self.parser, name)
467 b3dd8f4b Stavros Sachtouris
                self._parser_modified = True
468 e0da0f90 Stavros Sachtouris
            except ArgumentError:
469 e0da0f90 Stavros Sachtouris
                pass
470 e0da0f90 Stavros Sachtouris
471 7c2247a0 Stavros Sachtouris
    def update_arguments(self, new_arguments):
472 7c2247a0 Stavros Sachtouris
        """Add to / update existing arguments
473 7c2247a0 Stavros Sachtouris

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