Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument.py @ 9986e569

History | View | Annotate | Download (15.5 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 b696ed2c Stavros Sachtouris
from kamaki.cli.errors import CLISyntaxError, raiseCLIError
36 e0f40c94 Stavros Sachtouris
from kamaki.cli.utils import split_input
37 9986e569 Stavros Sachtouris
from kamaki.logger import get_logger
38 04d01cd4 Stavros Sachtouris
from datetime import datetime as dtm
39 ea4a21b8 Stavros Sachtouris
from time import mktime
40 db8d1766 Stavros Sachtouris
41 e0f40c94 Stavros Sachtouris
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 9986e569 Stavros Sachtouris
log = get_logger('kamaki.cli')
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 edb7fc1a Stavros Sachtouris
    This is the general 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 606fe15f Stavros Sachtouris
67 06eccea2 Stavros Sachtouris
        if help:
68 dfee2caf Stavros Sachtouris
            self.help = help
69 06eccea2 Stavros Sachtouris
        if parsed_name:
70 dfee2caf Stavros Sachtouris
            self.parsed_name = parsed_name
71 d00fd070 Stavros Sachtouris
        self.default = default
72 dfee2caf Stavros Sachtouris
73 fd5db045 Stavros Sachtouris
    @property
74 dfee2caf Stavros Sachtouris
    def parsed_name(self):
75 edb7fc1a Stavros Sachtouris
        """the string which will be recognised by the parser as an instance
76 edb7fc1a Stavros Sachtouris
            of this argument
77 edb7fc1a Stavros Sachtouris
        """
78 dfee2caf Stavros Sachtouris
        return getattr(self, '_parsed_name', None)
79 fd5db045 Stavros Sachtouris
80 dfee2caf Stavros Sachtouris
    @parsed_name.setter
81 dfee2caf Stavros Sachtouris
    def parsed_name(self, newname):
82 dfee2caf Stavros Sachtouris
        self._parsed_name = getattr(self, '_parsed_name', [])
83 dfee2caf Stavros Sachtouris
        if isinstance(newname, list) or isinstance(newname, tuple):
84 dfee2caf Stavros Sachtouris
            self._parsed_name += list(newname)
85 dfee2caf Stavros Sachtouris
        else:
86 a517ff50 Stavros Sachtouris
            self._parsed_name.append('%s' % newname)
87 dfee2caf Stavros Sachtouris
88 fd5db045 Stavros Sachtouris
    @property
89 dfee2caf Stavros Sachtouris
    def help(self):
90 edb7fc1a Stavros Sachtouris
        """a user friendly help message"""
91 dfee2caf Stavros Sachtouris
        return getattr(self, '_help', None)
92 fd5db045 Stavros Sachtouris
93 dfee2caf Stavros Sachtouris
    @help.setter
94 dfee2caf Stavros Sachtouris
    def help(self, newhelp):
95 a517ff50 Stavros Sachtouris
        self._help = '%s' % newhelp
96 dfee2caf Stavros Sachtouris
97 fd5db045 Stavros Sachtouris
    @property
98 dfee2caf Stavros Sachtouris
    def arity(self):
99 edb7fc1a Stavros Sachtouris
        """negative for repeating, 0 for flag, 1 or more for values"""
100 dfee2caf Stavros Sachtouris
        return getattr(self, '_arity', None)
101 fd5db045 Stavros Sachtouris
102 dfee2caf Stavros Sachtouris
    @arity.setter
103 dfee2caf Stavros Sachtouris
    def arity(self, newarity):
104 dfee2caf Stavros Sachtouris
        newarity = int(newarity)
105 dfee2caf Stavros Sachtouris
        self._arity = newarity
106 dfee2caf Stavros Sachtouris
107 fd5db045 Stavros Sachtouris
    @property
108 dfee2caf Stavros Sachtouris
    def default(self):
109 edb7fc1a Stavros Sachtouris
        """the value of this argument when not set"""
110 dfee2caf Stavros Sachtouris
        if not hasattr(self, '_default'):
111 dfee2caf Stavros Sachtouris
            self._default = False if self.arity == 0 else None
112 dfee2caf Stavros Sachtouris
        return self._default
113 fd5db045 Stavros Sachtouris
114 dfee2caf Stavros Sachtouris
    @default.setter
115 dfee2caf Stavros Sachtouris
    def default(self, newdefault):
116 dfee2caf Stavros Sachtouris
        self._default = newdefault
117 dfee2caf Stavros Sachtouris
118 fd5db045 Stavros Sachtouris
    @property
119 dfee2caf Stavros Sachtouris
    def value(self):
120 edb7fc1a Stavros Sachtouris
        """the value of the argument"""
121 dfee2caf Stavros Sachtouris
        return getattr(self, '_value', self.default)
122 fd5db045 Stavros Sachtouris
123 dfee2caf Stavros Sachtouris
    @value.setter
124 dfee2caf Stavros Sachtouris
    def value(self, newvalue):
125 dfee2caf Stavros Sachtouris
        self._value = newvalue
126 dfee2caf Stavros Sachtouris
127 dfee2caf Stavros Sachtouris
    def update_parser(self, parser, name):
128 edb7fc1a Stavros Sachtouris
        """Update argument parser with self info"""
129 0a0b9fb6 Stavros Sachtouris
        action = 'append' if self.arity < 0\
130 0a0b9fb6 Stavros Sachtouris
            else 'store_true' if self.arity == 0\
131 0a0b9fb6 Stavros Sachtouris
            else 'store'
132 de73876b Stavros Sachtouris
        parser.add_argument(
133 de73876b Stavros Sachtouris
            *self.parsed_name,
134 de73876b Stavros Sachtouris
            dest=name,
135 de73876b Stavros Sachtouris
            action=action,
136 de73876b Stavros Sachtouris
            default=self.default,
137 de73876b Stavros Sachtouris
            help=self.help)
138 dfee2caf Stavros Sachtouris
139 dfee2caf Stavros Sachtouris
    def main(self):
140 edb7fc1a Stavros Sachtouris
        """Overide this method to give functionality to your args"""
141 dfee2caf Stavros Sachtouris
        raise NotImplementedError
142 dfee2caf Stavros Sachtouris
143 fd5db045 Stavros Sachtouris
144 9bdc89da Stavros Sachtouris
class ConfigArgument(Argument):
145 439926dd Stavros Sachtouris
    """Manage a kamaki configuration (file)"""
146 edb7fc1a Stavros Sachtouris
147 e9a92550 Stavros Sachtouris
    _config_file = None
148 e9a92550 Stavros Sachtouris
149 fd5db045 Stavros Sachtouris
    @property
150 c41a86b2 Stavros Sachtouris
    def value(self):
151 439926dd Stavros Sachtouris
        """A Config object"""
152 d486baec Stavros Sachtouris
        super(self.__class__, self).value
153 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
154 fd5db045 Stavros Sachtouris
155 c41a86b2 Stavros Sachtouris
    @value.setter
156 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
157 e9a92550 Stavros Sachtouris
        if config_file:
158 e9a92550 Stavros Sachtouris
            self._value = Config(config_file)
159 e9a92550 Stavros Sachtouris
            self._config_file = config_file
160 e9a92550 Stavros Sachtouris
        elif self._config_file:
161 e9a92550 Stavros Sachtouris
            self._value = Config(self._config_file)
162 e9a92550 Stavros Sachtouris
        else:
163 e9a92550 Stavros Sachtouris
            self._value = Config()
164 9bdc89da Stavros Sachtouris
165 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
166 439926dd Stavros Sachtouris
        """Get a configuration setting from the Config object"""
167 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
168 c41a86b2 Stavros Sachtouris
169 c41a86b2 Stavros Sachtouris
    def get_groups(self):
170 c41a86b2 Stavros Sachtouris
        return self.value.apis()
171 017d37ce Stavros Sachtouris
172 1736e06d Stavros Sachtouris
_config_arg = ConfigArgument(
173 1736e06d Stavros Sachtouris
    1, 'Path to configuration file',
174 1736e06d Stavros Sachtouris
    ('-c', '--config'))
175 017d37ce Stavros Sachtouris
176 fd5db045 Stavros Sachtouris
177 9bdc89da Stavros Sachtouris
class CmdLineConfigArgument(Argument):
178 edb7fc1a Stavros Sachtouris
    """Set a run-time setting option (not persistent)"""
179 edb7fc1a Stavros Sachtouris
180 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
181 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
182 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
183 c41a86b2 Stavros Sachtouris
184 fd5db045 Stavros Sachtouris
    @property
185 c41a86b2 Stavros Sachtouris
    def value(self):
186 439926dd Stavros Sachtouris
        """A key=val option"""
187 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
188 fd5db045 Stavros Sachtouris
189 c41a86b2 Stavros Sachtouris
    @value.setter
190 c41a86b2 Stavros Sachtouris
    def value(self, options):
191 c41a86b2 Stavros Sachtouris
        if options == self.default:
192 c41a86b2 Stavros Sachtouris
            return
193 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
194 a517ff50 Stavros Sachtouris
            options = ['%s' % options]
195 c41a86b2 Stavros Sachtouris
        for option in options:
196 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
197 c41a86b2 Stavros Sachtouris
            if not sep:
198 de73876b Stavros Sachtouris
                raiseCLIError(
199 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument Syntax Error '),
200 24ff0a35 Stavros Sachtouris
                    details=[
201 24ff0a35 Stavros Sachtouris
                        '%s is missing a "="',
202 24ff0a35 Stavros Sachtouris
                        ' (usage: -o section.key=val)' % option])
203 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
204 0a0b9fb6 Stavros Sachtouris
        if not sep:
205 0a0b9fb6 Stavros Sachtouris
            key = section
206 0a0b9fb6 Stavros Sachtouris
            section = 'global'
207 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
208 0a0b9fb6 Stavros Sachtouris
            section.strip(),
209 fd5db045 Stavros Sachtouris
            key.strip(),
210 fd5db045 Stavros Sachtouris
            val.strip())
211 fd5db045 Stavros Sachtouris
212 c41a86b2 Stavros Sachtouris
213 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
214 edb7fc1a Stavros Sachtouris
    """
215 439926dd Stavros Sachtouris
    :value: true if set, false otherwise
216 edb7fc1a Stavros Sachtouris
    """
217 edb7fc1a Stavros Sachtouris
218 40a9c357 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=False):
219 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
220 c41a86b2 Stavros Sachtouris
221 fd5db045 Stavros Sachtouris
222 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
223 edb7fc1a Stavros Sachtouris
    """
224 edb7fc1a Stavros Sachtouris
    :value type: string
225 edb7fc1a Stavros Sachtouris
    :value returns: given value or default
226 edb7fc1a Stavros Sachtouris
    """
227 edb7fc1a Stavros Sachtouris
228 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
229 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
230 c41a86b2 Stavros Sachtouris
231 fd5db045 Stavros Sachtouris
232 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
233 edb7fc1a Stavros Sachtouris
234 fd5db045 Stavros Sachtouris
    @property
235 e3d4d442 Stavros Sachtouris
    def value(self):
236 439926dd Stavros Sachtouris
        """integer (type checking)"""
237 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
238 fd5db045 Stavros Sachtouris
239 e3d4d442 Stavros Sachtouris
    @value.setter
240 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
241 e3d4d442 Stavros Sachtouris
        if newvalue == self.default:
242 e3d4d442 Stavros Sachtouris
            self._value = self.default
243 e3d4d442 Stavros Sachtouris
            return
244 e3d4d442 Stavros Sachtouris
        try:
245 e3d4d442 Stavros Sachtouris
            self._value = int(newvalue)
246 e3d4d442 Stavros Sachtouris
        except ValueError:
247 24ff0a35 Stavros Sachtouris
            raiseCLIError(CLISyntaxError(
248 24ff0a35 Stavros Sachtouris
                'IntArgument Error',
249 c8e17a67 Stavros Sachtouris
                details=['Value %s not an int' % newvalue]))
250 fd5db045 Stavros Sachtouris
251 e3d4d442 Stavros Sachtouris
252 04d01cd4 Stavros Sachtouris
class DateArgument(ValueArgument):
253 04d01cd4 Stavros Sachtouris
    """
254 04d01cd4 Stavros Sachtouris
    :value type: a string formated in an acceptable date format
255 04d01cd4 Stavros Sachtouris

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

322 439926dd Stavros Sachtouris
    :syntax: --<arg> key1=value1 --<arg> key2=value2 ...
323 edb7fc1a Stavros Sachtouris
    """
324 edb7fc1a Stavros Sachtouris
325 0a0b9fb6 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
326 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
327 f3e94e06 Stavros Sachtouris
328 fd5db045 Stavros Sachtouris
    @property
329 f3e94e06 Stavros Sachtouris
    def value(self):
330 439926dd Stavros Sachtouris
        """
331 439926dd Stavros Sachtouris
        :input: key=value
332 439926dd Stavros Sachtouris
        :output: {'key1':'value1', 'key2':'value2', ...}
333 439926dd Stavros Sachtouris
        """
334 f3e94e06 Stavros Sachtouris
        return super(KeyValueArgument, self).value
335 fd5db045 Stavros Sachtouris
336 fd5db045 Stavros Sachtouris
    @value.setter
337 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
338 0a0b9fb6 Stavros Sachtouris
        self._value = {}
339 f3e94e06 Stavros Sachtouris
        for pair in keyvalue_pairs:
340 fd5db045 Stavros Sachtouris
            key, sep, val = pair.partition('=')
341 f3e94e06 Stavros Sachtouris
            if not sep:
342 de73876b Stavros Sachtouris
                raiseCLIError(
343 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument syntax error '),
344 0a0b9fb6 Stavros Sachtouris
                    details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
345 23803b28 Stavros Sachtouris
            self._value[key.strip()] = val.strip()
346 f3e94e06 Stavros Sachtouris
347 fd1f1d96 Stavros Sachtouris
348 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
349 439926dd Stavros Sachtouris
    """Manage a progress bar"""
350 fd1f1d96 Stavros Sachtouris
351 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
352 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
353 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
354 fd1f1d96 Stavros Sachtouris
        try:
355 1d329d27 Stavros Sachtouris
            KamakiProgressBar
356 fd1f1d96 Stavros Sachtouris
        except NameError:
357 9986e569 Stavros Sachtouris
            log.debug('WARNING: no progress bar functionality')
358 fd1f1d96 Stavros Sachtouris
359 852a22e7 Stavros Sachtouris
    def clone(self):
360 439926dd Stavros Sachtouris
        """Get a modifiable copy of this bar"""
361 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
362 852a22e7 Stavros Sachtouris
            self.help,
363 852a22e7 Stavros Sachtouris
            self.parsed_name,
364 852a22e7 Stavros Sachtouris
            self.default)
365 852a22e7 Stavros Sachtouris
        newarg._value = self._value
366 852a22e7 Stavros Sachtouris
        return newarg
367 852a22e7 Stavros Sachtouris
368 fd1f1d96 Stavros Sachtouris
    def get_generator(self, message, message_len=25):
369 439926dd Stavros Sachtouris
        """Get a generator to handle progress of the bar (gen.next())"""
370 fd1f1d96 Stavros Sachtouris
        if self.value:
371 fd1f1d96 Stavros Sachtouris
            return None
372 fd1f1d96 Stavros Sachtouris
        try:
373 1d329d27 Stavros Sachtouris
            self.bar = KamakiProgressBar()
374 fd1f1d96 Stavros Sachtouris
        except NameError:
375 329753ae Stavros Sachtouris
            self.value = None
376 329753ae Stavros Sachtouris
            return self.value
377 329753ae Stavros Sachtouris
        self.bar.message = message.ljust(message_len)
378 852a22e7 Stavros Sachtouris
        self.bar.suffix = '%(percent)d%% - %(eta)ds'
379 a10f5561 Stavros Sachtouris
        self.bar.start()
380 fd1f1d96 Stavros Sachtouris
381 852a22e7 Stavros Sachtouris
        def progress_gen(n):
382 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
383 852a22e7 Stavros Sachtouris
                yield
384 852a22e7 Stavros Sachtouris
            yield
385 852a22e7 Stavros Sachtouris
        return progress_gen
386 fd1f1d96 Stavros Sachtouris
387 852a22e7 Stavros Sachtouris
    def finish(self):
388 439926dd Stavros Sachtouris
        """Stop progress bar, return terminal cursor to user"""
389 852a22e7 Stavros Sachtouris
        if self.value:
390 852a22e7 Stavros Sachtouris
            return
391 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
392 852a22e7 Stavros Sachtouris
        if mybar:
393 852a22e7 Stavros Sachtouris
            mybar.finish()
394 fd1f1d96 Stavros Sachtouris
395 fd1f1d96 Stavros Sachtouris
396 de73876b Stavros Sachtouris
_arguments = dict(
397 de73876b Stavros Sachtouris
    config=_config_arg,
398 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
399 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
400 de73876b Stavros Sachtouris
    include=FlagArgument(
401 de73876b Stavros Sachtouris
        'Include raw connection data in the output',
402 fd5db045 Stavros Sachtouris
        ('-i', '--include')),
403 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
404 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
405 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
406 de73876b Stavros Sachtouris
    options=CmdLineConfigArgument(
407 de73876b Stavros Sachtouris
        _config_arg,
408 fd5db045 Stavros Sachtouris
        'Override a config value',
409 fd5db045 Stavros Sachtouris
        ('-o', '--options'))
410 9bdc89da Stavros Sachtouris
)
411 439926dd Stavros Sachtouris
"""Initial command line interface arguments"""
412 439926dd Stavros Sachtouris
413 439926dd Stavros Sachtouris
414 439926dd Stavros Sachtouris
"""
415 439926dd Stavros Sachtouris
Mechanism:
416 439926dd Stavros Sachtouris
    init_parser
417 439926dd Stavros Sachtouris
    parse_known_args
418 439926dd Stavros Sachtouris
    manage top-level user arguments input
419 439926dd Stavros Sachtouris
    find user-requested command
420 439926dd Stavros Sachtouris
    add command-specific arguments to dict
421 439926dd Stavros Sachtouris
    update_arguments
422 439926dd Stavros Sachtouris
"""
423 439926dd Stavros Sachtouris
424 439926dd Stavros Sachtouris
425 7c2247a0 Stavros Sachtouris
class ArgumentParseManager(object):
426 e0da0f90 Stavros Sachtouris
    """Manage (initialize and update) an ArgumentParser object"""
427 e0da0f90 Stavros Sachtouris
428 5ad77121 Stavros Sachtouris
    parser = None
429 7c2247a0 Stavros Sachtouris
    _arguments = {}
430 b3dd8f4b Stavros Sachtouris
    _parser_modified = False
431 b3dd8f4b Stavros Sachtouris
    _parsed = None
432 b3dd8f4b Stavros Sachtouris
    _unparsed = None
433 e0da0f90 Stavros Sachtouris
434 e0da0f90 Stavros Sachtouris
    def __init__(self, exe, arguments=None):
435 e0da0f90 Stavros Sachtouris
        """
436 e0da0f90 Stavros Sachtouris
        :param exe: (str) the basic command (e.g. 'kamaki')
437 e0da0f90 Stavros Sachtouris

438 e0da0f90 Stavros Sachtouris
        :param arguments: (dict) if given, overrides the global _argument as
439 e0da0f90 Stavros Sachtouris
            the parsers arguments specification
440 e0da0f90 Stavros Sachtouris
        """
441 de73876b Stavros Sachtouris
        self.parser = ArgumentParser(
442 de73876b Stavros Sachtouris
            add_help=False,
443 2703cceb Stavros Sachtouris
            formatter_class=RawDescriptionHelpFormatter)
444 b3dd8f4b Stavros Sachtouris
        self.syntax = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe
445 e0da0f90 Stavros Sachtouris
        if arguments:
446 e0da0f90 Stavros Sachtouris
            self.arguments = arguments
447 e0da0f90 Stavros Sachtouris
        else:
448 e0da0f90 Stavros Sachtouris
            global _arguments
449 e0da0f90 Stavros Sachtouris
            self.arguments = _arguments
450 7c2247a0 Stavros Sachtouris
        self.parse()
451 e0da0f90 Stavros Sachtouris
452 e0da0f90 Stavros Sachtouris
    @property
453 e0da0f90 Stavros Sachtouris
    def syntax(self):
454 b3dd8f4b Stavros Sachtouris
        """The command syntax (useful for help messages, descriptions, etc)"""
455 e0da0f90 Stavros Sachtouris
        return self.parser.prog
456 e0da0f90 Stavros Sachtouris
457 e0da0f90 Stavros Sachtouris
    @syntax.setter
458 e0da0f90 Stavros Sachtouris
    def syntax(self, new_syntax):
459 e0da0f90 Stavros Sachtouris
        self.parser.prog = new_syntax
460 e0da0f90 Stavros Sachtouris
461 b3dd8f4b Stavros Sachtouris
    @property
462 b3dd8f4b Stavros Sachtouris
    def arguments(self):
463 b3dd8f4b Stavros Sachtouris
        """(dict) arguments the parser should be aware of"""
464 b3dd8f4b Stavros Sachtouris
        return self._arguments
465 b3dd8f4b Stavros Sachtouris
466 b3dd8f4b Stavros Sachtouris
    @arguments.setter
467 b3dd8f4b Stavros Sachtouris
    def arguments(self, new_arguments):
468 b3dd8f4b Stavros Sachtouris
        if new_arguments:
469 b3dd8f4b Stavros Sachtouris
            assert isinstance(new_arguments, dict)
470 b3dd8f4b Stavros Sachtouris
        self._arguments = new_arguments
471 b3dd8f4b Stavros Sachtouris
        self.update_parser()
472 b3dd8f4b Stavros Sachtouris
473 7c2247a0 Stavros Sachtouris
    @property
474 b3dd8f4b Stavros Sachtouris
    def parsed(self):
475 7c2247a0 Stavros Sachtouris
        """(Namespace) parser-matched terms"""
476 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
477 b3dd8f4b Stavros Sachtouris
            self.parse()
478 b3dd8f4b Stavros Sachtouris
        return self._parsed
479 b3dd8f4b Stavros Sachtouris
480 b3dd8f4b Stavros Sachtouris
    @property
481 b3dd8f4b Stavros Sachtouris
    def unparsed(self):
482 b3dd8f4b Stavros Sachtouris
        """(list) parser-unmatched terms"""
483 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
484 b3dd8f4b Stavros Sachtouris
            self.parse()
485 b3dd8f4b Stavros Sachtouris
        return self._unparsed
486 b3dd8f4b Stavros Sachtouris
487 e0da0f90 Stavros Sachtouris
    def update_parser(self, arguments=None):
488 e0da0f90 Stavros Sachtouris
        """Load argument specifications to parser
489 e0da0f90 Stavros Sachtouris

490 e0da0f90 Stavros Sachtouris
        :param arguments: if not given, update self.arguments instead
491 e0da0f90 Stavros Sachtouris
        """
492 e0da0f90 Stavros Sachtouris
        if not arguments:
493 b3dd8f4b Stavros Sachtouris
            arguments = self._arguments
494 e0da0f90 Stavros Sachtouris
495 e0da0f90 Stavros Sachtouris
        for name, arg in arguments.items():
496 e0da0f90 Stavros Sachtouris
            try:
497 e0da0f90 Stavros Sachtouris
                arg.update_parser(self.parser, name)
498 b3dd8f4b Stavros Sachtouris
                self._parser_modified = True
499 e0da0f90 Stavros Sachtouris
            except ArgumentError:
500 e0da0f90 Stavros Sachtouris
                pass
501 e0da0f90 Stavros Sachtouris
502 7c2247a0 Stavros Sachtouris
    def update_arguments(self, new_arguments):
503 7c2247a0 Stavros Sachtouris
        """Add to / update existing arguments
504 7c2247a0 Stavros Sachtouris

505 7c2247a0 Stavros Sachtouris
        :param new_arguments: (dict)
506 7c2247a0 Stavros Sachtouris
        """
507 7c2247a0 Stavros Sachtouris
        if new_arguments:
508 7c2247a0 Stavros Sachtouris
            assert isinstance(new_arguments, dict)
509 7c2247a0 Stavros Sachtouris
            self._arguments.update(new_arguments)
510 7c2247a0 Stavros Sachtouris
            self.update_parser()
511 7c2247a0 Stavros Sachtouris
512 120126f1 Stavros Sachtouris
    def parse(self, new_args=None):
513 7c2247a0 Stavros Sachtouris
        """Do parse user input"""
514 c8e17a67 Stavros Sachtouris
        try:
515 c8e17a67 Stavros Sachtouris
            if new_args:
516 c8e17a67 Stavros Sachtouris
                self._parsed, unparsed = self.parser.parse_known_args(new_args)
517 c8e17a67 Stavros Sachtouris
            else:
518 c8e17a67 Stavros Sachtouris
                self._parsed, unparsed = self.parser.parse_known_args()
519 c8e17a67 Stavros Sachtouris
        except SystemExit:
520 c8e17a67 Stavros Sachtouris
            # deal with the fact that argparse error system is STUPID
521 c8e17a67 Stavros Sachtouris
            raiseCLIError(CLISyntaxError('Argument Syntax Error'))
522 b3dd8f4b Stavros Sachtouris
        for name, arg in self.arguments.items():
523 b3dd8f4b Stavros Sachtouris
            arg.value = getattr(self._parsed, name, arg.default)
524 b3dd8f4b Stavros Sachtouris
        self._unparsed = []
525 b3dd8f4b Stavros Sachtouris
        for term in unparsed:
526 b3dd8f4b Stavros Sachtouris
            self._unparsed += split_input(' \'%s\' ' % term)
527 b3dd8f4b Stavros Sachtouris
        self._parser_modified = False