Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument.py @ d160ada1

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 db8d1766 Stavros Sachtouris
from logging import getLogger
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 b6a99832 Stavros Sachtouris
kloger = getLogger('kamaki')
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 d160ada1 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 d160ada1 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 017d37ce Stavros Sachtouris
_config_arg = ConfigArgument(1, 'Path to configuration file', '--config')
173 017d37ce Stavros Sachtouris
174 fd5db045 Stavros Sachtouris
175 9bdc89da Stavros Sachtouris
class CmdLineConfigArgument(Argument):
176 edb7fc1a Stavros Sachtouris
    """Set a run-time setting option (not persistent)"""
177 edb7fc1a Stavros Sachtouris
178 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
179 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
180 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
181 c41a86b2 Stavros Sachtouris
182 fd5db045 Stavros Sachtouris
    @property
183 c41a86b2 Stavros Sachtouris
    def value(self):
184 439926dd Stavros Sachtouris
        """A key=val option"""
185 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
186 fd5db045 Stavros Sachtouris
187 c41a86b2 Stavros Sachtouris
    @value.setter
188 c41a86b2 Stavros Sachtouris
    def value(self, options):
189 c41a86b2 Stavros Sachtouris
        if options == self.default:
190 c41a86b2 Stavros Sachtouris
            return
191 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
192 d160ada1 Stavros Sachtouris
            options = ['%s' % options]
193 c41a86b2 Stavros Sachtouris
        for option in options:
194 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
195 c41a86b2 Stavros Sachtouris
            if not sep:
196 de73876b Stavros Sachtouris
                raiseCLIError(
197 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument Syntax Error '),
198 24ff0a35 Stavros Sachtouris
                    details=[
199 24ff0a35 Stavros Sachtouris
                        '%s is missing a "="',
200 24ff0a35 Stavros Sachtouris
                        ' (usage: -o section.key=val)' % option])
201 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
202 0a0b9fb6 Stavros Sachtouris
        if not sep:
203 0a0b9fb6 Stavros Sachtouris
            key = section
204 0a0b9fb6 Stavros Sachtouris
            section = 'global'
205 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
206 0a0b9fb6 Stavros Sachtouris
            section.strip(),
207 fd5db045 Stavros Sachtouris
            key.strip(),
208 fd5db045 Stavros Sachtouris
            val.strip())
209 fd5db045 Stavros Sachtouris
210 c41a86b2 Stavros Sachtouris
211 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
212 edb7fc1a Stavros Sachtouris
    """
213 439926dd Stavros Sachtouris
    :value: true if set, false otherwise
214 edb7fc1a Stavros Sachtouris
    """
215 edb7fc1a Stavros Sachtouris
216 40a9c357 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=False):
217 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
218 c41a86b2 Stavros Sachtouris
219 fd5db045 Stavros Sachtouris
220 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
221 edb7fc1a Stavros Sachtouris
    """
222 edb7fc1a Stavros Sachtouris
    :value type: string
223 edb7fc1a Stavros Sachtouris
    :value returns: given value or default
224 edb7fc1a Stavros Sachtouris
    """
225 edb7fc1a Stavros Sachtouris
226 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
227 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
228 c41a86b2 Stavros Sachtouris
229 fd5db045 Stavros Sachtouris
230 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
231 edb7fc1a Stavros Sachtouris
232 fd5db045 Stavros Sachtouris
    @property
233 e3d4d442 Stavros Sachtouris
    def value(self):
234 439926dd Stavros Sachtouris
        """integer (type checking)"""
235 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
236 fd5db045 Stavros Sachtouris
237 e3d4d442 Stavros Sachtouris
    @value.setter
238 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
239 e3d4d442 Stavros Sachtouris
        if newvalue == self.default:
240 e3d4d442 Stavros Sachtouris
            self._value = self.default
241 e3d4d442 Stavros Sachtouris
            return
242 e3d4d442 Stavros Sachtouris
        try:
243 e3d4d442 Stavros Sachtouris
            self._value = int(newvalue)
244 e3d4d442 Stavros Sachtouris
        except ValueError:
245 24ff0a35 Stavros Sachtouris
            raiseCLIError(CLISyntaxError(
246 24ff0a35 Stavros Sachtouris
                'IntArgument Error',
247 c8e17a67 Stavros Sachtouris
                details=['Value %s not an int' % newvalue]))
248 fd5db045 Stavros Sachtouris
249 e3d4d442 Stavros Sachtouris
250 04d01cd4 Stavros Sachtouris
class DateArgument(ValueArgument):
251 04d01cd4 Stavros Sachtouris
    """
252 04d01cd4 Stavros Sachtouris
    :value type: a string formated in an acceptable date format
253 04d01cd4 Stavros Sachtouris

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

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

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

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

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