Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument / __init__.py @ bb50c4ec

History | View | Annotate | Download (25.3 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2 b9331a9f Stavros Sachtouris
#
3 b9331a9f Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 b9331a9f Stavros Sachtouris
# without modification, are permitted provided that the following
5 b9331a9f Stavros Sachtouris
# conditions are met:
6 b9331a9f Stavros Sachtouris
#
7 b9331a9f Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 b9331a9f Stavros Sachtouris
#     copyright notice, this list of conditions and the following
9 b9331a9f Stavros Sachtouris
#     disclaimer.
10 b9331a9f Stavros Sachtouris
#
11 b9331a9f Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 b9331a9f Stavros Sachtouris
#     copyright notice, this list of conditions and the following
13 b9331a9f Stavros Sachtouris
#     disclaimer in the documentation and/or other materials
14 b9331a9f Stavros Sachtouris
#     provided with the distribution.
15 b9331a9f Stavros Sachtouris
#
16 b9331a9f Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 b9331a9f Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 b9331a9f Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 b9331a9f Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 b9331a9f Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 b9331a9f Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 b9331a9f Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 b9331a9f Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 b9331a9f Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 b9331a9f Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 b9331a9f Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 b9331a9f Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 b9331a9f Stavros Sachtouris
#
29 b9331a9f Stavros Sachtouris
# The views and conclusions contained in the software and
30 b9331a9f Stavros Sachtouris
# documentation are those of the authors and should not be
31 b9331a9f Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 b9331a9f Stavros Sachtouris
# or implied, of GRNET S.A.
33 9bdc89da Stavros Sachtouris
34 c270fe96 Stavros Sachtouris
from kamaki.cli.config import Config
35 c3d42104 Stavros Sachtouris
from kamaki.cli.errors import (
36 c3d42104 Stavros Sachtouris
    CLISyntaxError, raiseCLIError, CLIInvalidArgument)
37 7b109aa7 Stavros Sachtouris
from kamaki.cli.utils import split_input, to_bytes
38 7637d600 Stavros Sachtouris
39 04d01cd4 Stavros Sachtouris
from datetime import datetime as dtm
40 ea4a21b8 Stavros Sachtouris
from time import mktime
41 56d84a4e Stavros Sachtouris
from sys import stderr
42 db8d1766 Stavros Sachtouris
43 7637d600 Stavros Sachtouris
from logging import getLogger
44 b33d9050 Stavros Sachtouris
from argparse import (
45 b33d9050 Stavros Sachtouris
    ArgumentParser, ArgumentError, RawDescriptionHelpFormatter)
46 67083ca0 Stavros Sachtouris
from progress.bar import ShadyBar as KamakiProgressBar
47 fd1f1d96 Stavros Sachtouris
48 7637d600 Stavros Sachtouris
log = getLogger(__name__)
49 db8d1766 Stavros Sachtouris
50 fd5db045 Stavros Sachtouris
51 b33d9050 Stavros Sachtouris
class NoAbbrArgumentParser(ArgumentParser):
52 b33d9050 Stavros Sachtouris
    """This is Argument Parser with disabled argument abbreviation"""
53 b33d9050 Stavros Sachtouris
54 b33d9050 Stavros Sachtouris
    def _get_option_tuples(self, option_string):
55 b33d9050 Stavros Sachtouris
        result = []
56 b33d9050 Stavros Sachtouris
        chars = self.prefix_chars
57 b33d9050 Stavros Sachtouris
        if option_string[0] in chars and option_string[1] in chars:
58 b33d9050 Stavros Sachtouris
            if '=' in option_string:
59 b33d9050 Stavros Sachtouris
                option_prefix, explicit_arg = option_string.split('=', 1)
60 b33d9050 Stavros Sachtouris
            else:
61 b33d9050 Stavros Sachtouris
                option_prefix = option_string
62 b33d9050 Stavros Sachtouris
                explicit_arg = None
63 b33d9050 Stavros Sachtouris
            for option_string in self._option_string_actions:
64 b33d9050 Stavros Sachtouris
                if option_string == option_prefix:
65 b33d9050 Stavros Sachtouris
                    action = self._option_string_actions[option_string]
66 b33d9050 Stavros Sachtouris
                    tup = action, option_string, explicit_arg
67 b33d9050 Stavros Sachtouris
                    result.append(tup)
68 b33d9050 Stavros Sachtouris
        elif option_string[0] in chars and option_string[1] not in chars:
69 b33d9050 Stavros Sachtouris
            option_prefix = option_string
70 b33d9050 Stavros Sachtouris
            explicit_arg = None
71 b33d9050 Stavros Sachtouris
            short_option_prefix = option_string[:2]
72 b33d9050 Stavros Sachtouris
            short_explicit_arg = option_string[2:]
73 b33d9050 Stavros Sachtouris
74 b33d9050 Stavros Sachtouris
            for option_string in self._option_string_actions:
75 b33d9050 Stavros Sachtouris
                if option_string == short_option_prefix:
76 b33d9050 Stavros Sachtouris
                    action = self._option_string_actions[option_string]
77 b33d9050 Stavros Sachtouris
                    tup = action, option_string, short_explicit_arg
78 b33d9050 Stavros Sachtouris
                    result.append(tup)
79 b33d9050 Stavros Sachtouris
                elif option_string == option_prefix:
80 b33d9050 Stavros Sachtouris
                    action = self._option_string_actions[option_string]
81 b33d9050 Stavros Sachtouris
                    tup = action, option_string, explicit_arg
82 b33d9050 Stavros Sachtouris
                    result.append(tup)
83 b33d9050 Stavros Sachtouris
        else:
84 b33d9050 Stavros Sachtouris
            return super(
85 b33d9050 Stavros Sachtouris
                NoAbbrArgumentParser, self)._get_option_tuples(option_string)
86 b33d9050 Stavros Sachtouris
        return result
87 b33d9050 Stavros Sachtouris
88 b33d9050 Stavros Sachtouris
89 dfee2caf Stavros Sachtouris
class Argument(object):
90 edb7fc1a Stavros Sachtouris
    """An argument that can be parsed from command line or otherwise.
91 5286e2c3 Stavros Sachtouris
    This is the top-level Argument class. It is suggested to extent this
92 edb7fc1a Stavros Sachtouris
    class into more specific argument types.
93 edb7fc1a Stavros Sachtouris
    """
94 eb647cfe Stavros Sachtouris
    lvalue_delimiter = '/'
95 dfee2caf Stavros Sachtouris
96 dfee2caf Stavros Sachtouris
    def __init__(self, arity, help=None, parsed_name=None, default=None):
97 606fe15f Stavros Sachtouris
        self.arity = int(arity)
98 5286e2c3 Stavros Sachtouris
        self.help = '%s' % help or ''
99 dfee2caf Stavros Sachtouris
100 f17d6cb5 Stavros Sachtouris
        assert parsed_name, 'No parsed name for argument %s' % self
101 f17d6cb5 Stavros Sachtouris
        self.parsed_name = list(parsed_name) if isinstance(
102 f17d6cb5 Stavros Sachtouris
            parsed_name, list) or isinstance(parsed_name, tuple) else (
103 f17d6cb5 Stavros Sachtouris
                '%s' % parsed_name).split()
104 f17d6cb5 Stavros Sachtouris
        for name in self.parsed_name:
105 f17d6cb5 Stavros Sachtouris
            assert name.count(' ') == 0, '%s: Invalid parse name "%s"' % (
106 f17d6cb5 Stavros Sachtouris
                self, name)
107 f17d6cb5 Stavros Sachtouris
            msg = '%s: Invalid parse name "%s" should start with a "-"' % (
108 f17d6cb5 Stavros Sachtouris
                    self, name)
109 f17d6cb5 Stavros Sachtouris
            assert name.startswith('-'), msg
110 f17d6cb5 Stavros Sachtouris
111 a6d2ad78 Stavros Sachtouris
        self.default = default or None
112 dfee2caf Stavros Sachtouris
113 fd5db045 Stavros Sachtouris
    @property
114 dfee2caf Stavros Sachtouris
    def value(self):
115 dfee2caf Stavros Sachtouris
        return getattr(self, '_value', self.default)
116 fd5db045 Stavros Sachtouris
117 dfee2caf Stavros Sachtouris
    @value.setter
118 dfee2caf Stavros Sachtouris
    def value(self, newvalue):
119 dfee2caf Stavros Sachtouris
        self._value = newvalue
120 dfee2caf Stavros Sachtouris
121 dfee2caf Stavros Sachtouris
    def update_parser(self, parser, name):
122 edb7fc1a Stavros Sachtouris
        """Update argument parser with self info"""
123 f17d6cb5 Stavros Sachtouris
        action = 'append' if self.arity < 0 else (
124 f17d6cb5 Stavros Sachtouris
            'store' if self.arity else 'store_true')
125 de73876b Stavros Sachtouris
        parser.add_argument(
126 de73876b Stavros Sachtouris
            *self.parsed_name,
127 f17d6cb5 Stavros Sachtouris
            dest=name, action=action, default=self.default, help=self.help)
128 dfee2caf Stavros Sachtouris
129 eb647cfe Stavros Sachtouris
    @property
130 eb647cfe Stavros Sachtouris
    def lvalue(self):
131 eb647cfe Stavros Sachtouris
        """A printable form of the left value when calling an argument e.g.,
132 eb647cfe Stavros Sachtouris
        --left-value=right-value"""
133 eb647cfe Stavros Sachtouris
        return (self.lvalue_delimiter or ' ').join(self.parsed_name or [])
134 eb647cfe Stavros Sachtouris
135 fd5db045 Stavros Sachtouris
136 9bdc89da Stavros Sachtouris
class ConfigArgument(Argument):
137 439926dd Stavros Sachtouris
    """Manage a kamaki configuration (file)"""
138 edb7fc1a Stavros Sachtouris
139 a7aacf12 Stavros Sachtouris
    def __init__(self, help, parsed_name=('-c', '--config')):
140 a7aacf12 Stavros Sachtouris
        super(ConfigArgument, self).__init__(1, help, parsed_name, None)
141 a7aacf12 Stavros Sachtouris
        self.file_path = None
142 e9a92550 Stavros Sachtouris
143 fd5db045 Stavros Sachtouris
    @property
144 c41a86b2 Stavros Sachtouris
    def value(self):
145 c22183b9 Stavros Sachtouris
        return getattr(self, '_value', None)
146 fd5db045 Stavros Sachtouris
147 c41a86b2 Stavros Sachtouris
    @value.setter
148 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
149 e9a92550 Stavros Sachtouris
        if config_file:
150 e9a92550 Stavros Sachtouris
            self._value = Config(config_file)
151 a7aacf12 Stavros Sachtouris
            self.file_path = config_file
152 a7aacf12 Stavros Sachtouris
        elif self.file_path:
153 a7aacf12 Stavros Sachtouris
            self._value = Config(self.file_path)
154 e9a92550 Stavros Sachtouris
        else:
155 e9a92550 Stavros Sachtouris
            self._value = Config()
156 9bdc89da Stavros Sachtouris
157 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
158 439926dd Stavros Sachtouris
        """Get a configuration setting from the Config object"""
159 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
160 c41a86b2 Stavros Sachtouris
161 a7aacf12 Stavros Sachtouris
    @property
162 a7aacf12 Stavros Sachtouris
    def groups(self):
163 362adf50 Stavros Sachtouris
        suffix = '_cli'
164 362adf50 Stavros Sachtouris
        slen = len(suffix)
165 362adf50 Stavros Sachtouris
        return [term[:-slen] for term in self.value.keys('global') if (
166 362adf50 Stavros Sachtouris
            term.endswith(suffix))]
167 f724cd35 Stavros Sachtouris
168 a7aacf12 Stavros Sachtouris
    @property
169 a7aacf12 Stavros Sachtouris
    def cli_specs(self):
170 362adf50 Stavros Sachtouris
        suffix = '_cli'
171 362adf50 Stavros Sachtouris
        slen = len(suffix)
172 362adf50 Stavros Sachtouris
        return [(k[:-slen], v) for k, v in self.value.items('global') if (
173 362adf50 Stavros Sachtouris
            k.endswith(suffix))]
174 362adf50 Stavros Sachtouris
175 362adf50 Stavros Sachtouris
    def get_global(self, option):
176 534e7bbb Stavros Sachtouris
        return self.value.get('global', option)
177 362adf50 Stavros Sachtouris
178 144b3551 Stavros Sachtouris
    def get_cloud(self, cloud, option):
179 144b3551 Stavros Sachtouris
        return self.value.get_cloud(cloud, option)
180 017d37ce Stavros Sachtouris
181 f17d6cb5 Stavros Sachtouris
182 a7aacf12 Stavros Sachtouris
_config_arg = ConfigArgument('Path to config file')
183 017d37ce Stavros Sachtouris
184 fd5db045 Stavros Sachtouris
185 1bd4f765 Stavros Sachtouris
class RuntimeConfigArgument(Argument):
186 edb7fc1a Stavros Sachtouris
    """Set a run-time setting option (not persistent)"""
187 edb7fc1a Stavros Sachtouris
188 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
189 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
190 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
191 c41a86b2 Stavros Sachtouris
192 fd5db045 Stavros Sachtouris
    @property
193 c41a86b2 Stavros Sachtouris
    def value(self):
194 34c480f2 Stavros Sachtouris
        return super(RuntimeConfigArgument, self).value
195 fd5db045 Stavros Sachtouris
196 c41a86b2 Stavros Sachtouris
    @value.setter
197 c41a86b2 Stavros Sachtouris
    def value(self, options):
198 c41a86b2 Stavros Sachtouris
        if options == self.default:
199 c41a86b2 Stavros Sachtouris
            return
200 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
201 a517ff50 Stavros Sachtouris
            options = ['%s' % options]
202 c41a86b2 Stavros Sachtouris
        for option in options:
203 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
204 c41a86b2 Stavros Sachtouris
            if not sep:
205 de73876b Stavros Sachtouris
                raiseCLIError(
206 de73876b Stavros Sachtouris
                    CLISyntaxError('Argument Syntax Error '),
207 24ff0a35 Stavros Sachtouris
                    details=[
208 24ff0a35 Stavros Sachtouris
                        '%s is missing a "="',
209 24ff0a35 Stavros Sachtouris
                        ' (usage: -o section.key=val)' % option])
210 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
211 0a0b9fb6 Stavros Sachtouris
        if not sep:
212 0a0b9fb6 Stavros Sachtouris
            key = section
213 0a0b9fb6 Stavros Sachtouris
            section = 'global'
214 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
215 0a0b9fb6 Stavros Sachtouris
            section.strip(),
216 fd5db045 Stavros Sachtouris
            key.strip(),
217 fd5db045 Stavros Sachtouris
            val.strip())
218 fd5db045 Stavros Sachtouris
219 c41a86b2 Stavros Sachtouris
220 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
221 edb7fc1a Stavros Sachtouris
    """
222 439926dd Stavros Sachtouris
    :value: true if set, false otherwise
223 edb7fc1a Stavros Sachtouris
    """
224 edb7fc1a Stavros Sachtouris
225 a6d2ad78 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
226 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
227 c41a86b2 Stavros Sachtouris
228 fd5db045 Stavros Sachtouris
229 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
230 edb7fc1a Stavros Sachtouris
    """
231 edb7fc1a Stavros Sachtouris
    :value type: string
232 edb7fc1a Stavros Sachtouris
    :value returns: given value or default
233 edb7fc1a Stavros Sachtouris
    """
234 edb7fc1a Stavros Sachtouris
235 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
236 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
237 c41a86b2 Stavros Sachtouris
238 fd5db045 Stavros Sachtouris
239 0155548b Stavros Sachtouris
class CommaSeparatedListArgument(ValueArgument):
240 0155548b Stavros Sachtouris
    """
241 0155548b Stavros Sachtouris
    :value type: string
242 0155548b Stavros Sachtouris
    :value returns: list of the comma separated values
243 0155548b Stavros Sachtouris
    """
244 0155548b Stavros Sachtouris
245 0155548b Stavros Sachtouris
    @property
246 0155548b Stavros Sachtouris
    def value(self):
247 0155548b Stavros Sachtouris
        return self._value or list()
248 0155548b Stavros Sachtouris
249 0155548b Stavros Sachtouris
    @value.setter
250 0155548b Stavros Sachtouris
    def value(self, newvalue):
251 0155548b Stavros Sachtouris
        self._value = newvalue.split(',') if newvalue else list()
252 0155548b Stavros Sachtouris
253 0155548b Stavros Sachtouris
254 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
255 edb7fc1a Stavros Sachtouris
256 fd5db045 Stavros Sachtouris
    @property
257 e3d4d442 Stavros Sachtouris
    def value(self):
258 439926dd Stavros Sachtouris
        """integer (type checking)"""
259 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
260 fd5db045 Stavros Sachtouris
261 e3d4d442 Stavros Sachtouris
    @value.setter
262 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
263 4a25486d Stavros Sachtouris
        if newvalue == self.default:
264 4a25486d Stavros Sachtouris
            self._value = newvalue
265 4a25486d Stavros Sachtouris
            return
266 e3d4d442 Stavros Sachtouris
        try:
267 4a25486d Stavros Sachtouris
            if int(newvalue) == float(newvalue):
268 4a25486d Stavros Sachtouris
                self._value = int(newvalue)
269 4a25486d Stavros Sachtouris
            else:
270 4a25486d Stavros Sachtouris
                raise ValueError('Raise int argument error')
271 e3d4d442 Stavros Sachtouris
        except ValueError:
272 24ff0a35 Stavros Sachtouris
            raiseCLIError(CLISyntaxError(
273 24ff0a35 Stavros Sachtouris
                'IntArgument Error',
274 c8e17a67 Stavros Sachtouris
                details=['Value %s not an int' % newvalue]))
275 fd5db045 Stavros Sachtouris
276 e3d4d442 Stavros Sachtouris
277 7b109aa7 Stavros Sachtouris
class DataSizeArgument(ValueArgument):
278 7b109aa7 Stavros Sachtouris
    """Input: a string of the form <number><unit>
279 7b109aa7 Stavros Sachtouris
    Output: the number of bytes
280 7b109aa7 Stavros Sachtouris
    Units: B, KiB, KB, MiB, MB, GiB, GB, TiB, TB
281 7b109aa7 Stavros Sachtouris
    """
282 7b109aa7 Stavros Sachtouris
283 7b109aa7 Stavros Sachtouris
    @property
284 7b109aa7 Stavros Sachtouris
    def value(self):
285 7b109aa7 Stavros Sachtouris
        return getattr(self, '_value', self.default)
286 7b109aa7 Stavros Sachtouris
287 7b109aa7 Stavros Sachtouris
    def _calculate_limit(self, user_input):
288 7b109aa7 Stavros Sachtouris
        limit = 0
289 7b109aa7 Stavros Sachtouris
        try:
290 7b109aa7 Stavros Sachtouris
            limit = int(user_input)
291 7b109aa7 Stavros Sachtouris
        except ValueError:
292 7b109aa7 Stavros Sachtouris
            index = 0
293 7b109aa7 Stavros Sachtouris
            digits = [str(num) for num in range(0, 10)] + ['.']
294 7b109aa7 Stavros Sachtouris
            while user_input[index] in digits:
295 7b109aa7 Stavros Sachtouris
                index += 1
296 7b109aa7 Stavros Sachtouris
            limit = user_input[:index]
297 7b109aa7 Stavros Sachtouris
            format = user_input[index:]
298 7b109aa7 Stavros Sachtouris
            try:
299 7b109aa7 Stavros Sachtouris
                return to_bytes(limit, format)
300 7b109aa7 Stavros Sachtouris
            except Exception as qe:
301 7b109aa7 Stavros Sachtouris
                msg = 'Failed to convert %s to bytes' % user_input,
302 7b109aa7 Stavros Sachtouris
                raiseCLIError(qe, msg, details=[
303 7b109aa7 Stavros Sachtouris
                    'Syntax: containerlimit set <limit>[format] [container]',
304 7b109aa7 Stavros Sachtouris
                    'e.g.,: containerlimit set 2.3GB mycontainer',
305 7b109aa7 Stavros Sachtouris
                    'Valid formats:',
306 7b109aa7 Stavros Sachtouris
                    '(*1024): B, KiB, MiB, GiB, TiB',
307 7b109aa7 Stavros Sachtouris
                    '(*1000): B, KB, MB, GB, TB'])
308 7b109aa7 Stavros Sachtouris
        return limit
309 7b109aa7 Stavros Sachtouris
310 7b109aa7 Stavros Sachtouris
    @value.setter
311 7b109aa7 Stavros Sachtouris
    def value(self, new_value):
312 7b109aa7 Stavros Sachtouris
        if new_value:
313 7b109aa7 Stavros Sachtouris
            self._value = self._calculate_limit(new_value)
314 7b109aa7 Stavros Sachtouris
315 7b109aa7 Stavros Sachtouris
316 023d5ada Stavros Sachtouris
class UserAccountArgument(ValueArgument):
317 023d5ada Stavros Sachtouris
    """A user UUID or name (if uuid does not exist)"""
318 023d5ada Stavros Sachtouris
319 023d5ada Stavros Sachtouris
    account_client = None
320 023d5ada Stavros Sachtouris
321 023d5ada Stavros Sachtouris
    @property
322 023d5ada Stavros Sachtouris
    def value(self):
323 023d5ada Stavros Sachtouris
        return super(UserAccountArgument, self).value
324 023d5ada Stavros Sachtouris
325 023d5ada Stavros Sachtouris
    @value.setter
326 023d5ada Stavros Sachtouris
    def value(self, uuid_or_name):
327 023d5ada Stavros Sachtouris
        if uuid_or_name and self.account_client:
328 023d5ada Stavros Sachtouris
            r = self.account_client.uuids2usernames([uuid_or_name, ])
329 023d5ada Stavros Sachtouris
            if r:
330 023d5ada Stavros Sachtouris
                self._value = uuid_or_name
331 023d5ada Stavros Sachtouris
            else:
332 023d5ada Stavros Sachtouris
                r = self.account_client.usernames2uuids([uuid_or_name])
333 023d5ada Stavros Sachtouris
                self._value = r.get(uuid_or_name) if r else None
334 023d5ada Stavros Sachtouris
            if not self._value:
335 023d5ada Stavros Sachtouris
                raise raiseCLIError('User name or UUID not found', details=[
336 023d5ada Stavros Sachtouris
                    '%s is not a known username or UUID' % uuid_or_name,
337 023d5ada Stavros Sachtouris
                    'Usage:  %s <USER_UUID | USERNAME>' % self.lvalue])
338 023d5ada Stavros Sachtouris
339 023d5ada Stavros Sachtouris
340 04d01cd4 Stavros Sachtouris
class DateArgument(ValueArgument):
341 04d01cd4 Stavros Sachtouris
342 b7ff6e0c Stavros Sachtouris
    DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
343 04d01cd4 Stavros Sachtouris
344 b7ff6e0c Stavros Sachtouris
    INPUT_FORMATS = [DATE_FORMAT, '%d-%m-%Y', '%H:%M:%S %d-%m-%Y']
345 04d01cd4 Stavros Sachtouris
346 04d01cd4 Stavros Sachtouris
    @property
347 ea4a21b8 Stavros Sachtouris
    def timestamp(self):
348 ea4a21b8 Stavros Sachtouris
        v = getattr(self, '_value', self.default)
349 ea4a21b8 Stavros Sachtouris
        return mktime(v.timetuple()) if v else None
350 ea4a21b8 Stavros Sachtouris
351 ea4a21b8 Stavros Sachtouris
    @property
352 ea4a21b8 Stavros Sachtouris
    def formated(self):
353 ea4a21b8 Stavros Sachtouris
        v = getattr(self, '_value', self.default)
354 b7ff6e0c Stavros Sachtouris
        return v.strftime(self.DATE_FORMAT) if v else None
355 ea4a21b8 Stavros Sachtouris
356 ea4a21b8 Stavros Sachtouris
    @property
357 04d01cd4 Stavros Sachtouris
    def value(self):
358 ea4a21b8 Stavros Sachtouris
        return self.timestamp
359 04d01cd4 Stavros Sachtouris
360 04d01cd4 Stavros Sachtouris
    @value.setter
361 04d01cd4 Stavros Sachtouris
    def value(self, newvalue):
362 2af87afc Stavros Sachtouris
        self._value = self.format_date(newvalue) if newvalue else self.default
363 04d01cd4 Stavros Sachtouris
364 04d01cd4 Stavros Sachtouris
    def format_date(self, datestr):
365 04d01cd4 Stavros Sachtouris
        for format in self.INPUT_FORMATS:
366 04d01cd4 Stavros Sachtouris
            try:
367 04d01cd4 Stavros Sachtouris
                t = dtm.strptime(datestr, format)
368 04d01cd4 Stavros Sachtouris
            except ValueError:
369 04d01cd4 Stavros Sachtouris
                continue
370 b7ff6e0c Stavros Sachtouris
            return t  # .strftime(self.DATE_FORMAT)
371 b7ff6e0c Stavros Sachtouris
        raiseCLIError(None, 'Date Argument Error', details=[
372 b7ff6e0c Stavros Sachtouris
            '%s not a valid date' % datestr,
373 b7ff6e0c Stavros Sachtouris
            'Correct formats:\n\t%s' % self.INPUT_FORMATS])
374 04d01cd4 Stavros Sachtouris
375 04d01cd4 Stavros Sachtouris
376 c41a86b2 Stavros Sachtouris
class VersionArgument(FlagArgument):
377 edb7fc1a Stavros Sachtouris
    """A flag argument with that prints current version"""
378 edb7fc1a Stavros Sachtouris
379 fd5db045 Stavros Sachtouris
    @property
380 c41a86b2 Stavros Sachtouris
    def value(self):
381 439926dd Stavros Sachtouris
        """bool"""
382 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
383 fd5db045 Stavros Sachtouris
384 c41a86b2 Stavros Sachtouris
    @value.setter
385 c41a86b2 Stavros Sachtouris
    def value(self, newvalue):
386 c41a86b2 Stavros Sachtouris
        self._value = newvalue
387 f17d6cb5 Stavros Sachtouris
        if newvalue:
388 c41a86b2 Stavros Sachtouris
            import kamaki
389 fd5db045 Stavros Sachtouris
            print('kamaki %s' % kamaki.__version__)
390 fd5db045 Stavros Sachtouris
391 9bdc89da Stavros Sachtouris
392 ca5528f1 Stavros Sachtouris
class RepeatableArgument(Argument):
393 ca5528f1 Stavros Sachtouris
    """A value argument that can be repeated"""
394 ca5528f1 Stavros Sachtouris
395 a6d2ad78 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
396 ca5528f1 Stavros Sachtouris
        super(RepeatableArgument, self).__init__(
397 ca5528f1 Stavros Sachtouris
            -1, help, parsed_name, default)
398 ca5528f1 Stavros Sachtouris
399 a6d2ad78 Stavros Sachtouris
    @property
400 a6d2ad78 Stavros Sachtouris
    def value(self):
401 a6d2ad78 Stavros Sachtouris
        return getattr(self, '_value', [])
402 a6d2ad78 Stavros Sachtouris
403 a6d2ad78 Stavros Sachtouris
    @value.setter
404 a6d2ad78 Stavros Sachtouris
    def value(self, newvalue):
405 a6d2ad78 Stavros Sachtouris
        self._value = newvalue
406 a6d2ad78 Stavros Sachtouris
407 ca5528f1 Stavros Sachtouris
408 0a0b9fb6 Stavros Sachtouris
class KeyValueArgument(Argument):
409 ca5528f1 Stavros Sachtouris
    """A Key=Value Argument that can be repeated
410 edb7fc1a Stavros Sachtouris

411 439926dd Stavros Sachtouris
    :syntax: --<arg> key1=value1 --<arg> key2=value2 ...
412 edb7fc1a Stavros Sachtouris
    """
413 edb7fc1a Stavros Sachtouris
414 a6d2ad78 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
415 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
416 f3e94e06 Stavros Sachtouris
417 fd5db045 Stavros Sachtouris
    @property
418 f3e94e06 Stavros Sachtouris
    def value(self):
419 439926dd Stavros Sachtouris
        """
420 8d427cb9 Stavros Sachtouris
        :returns: (dict) {key1: val1, key2: val2, ...}
421 439926dd Stavros Sachtouris
        """
422 a6d2ad78 Stavros Sachtouris
        return getattr(self, '_value', {})
423 fd5db045 Stavros Sachtouris
424 fd5db045 Stavros Sachtouris
    @value.setter
425 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
426 8d427cb9 Stavros Sachtouris
        """
427 8d427cb9 Stavros Sachtouris
        :param keyvalue_pairs: (str) ['key1=val1', 'key2=val2', ...]
428 8d427cb9 Stavros Sachtouris
        """
429 a6d2ad78 Stavros Sachtouris
        if keyvalue_pairs:
430 a6d2ad78 Stavros Sachtouris
            self._value = self.value
431 a6d2ad78 Stavros Sachtouris
            try:
432 a6d2ad78 Stavros Sachtouris
                for pair in keyvalue_pairs:
433 a6d2ad78 Stavros Sachtouris
                    key, sep, val = pair.partition('=')
434 a6d2ad78 Stavros Sachtouris
                    assert sep, ' %s misses a "=" (usage: key1=val1 )\n' % (
435 a6d2ad78 Stavros Sachtouris
                        pair)
436 a6d2ad78 Stavros Sachtouris
                    self._value[key] = val
437 a6d2ad78 Stavros Sachtouris
            except Exception as e:
438 a6d2ad78 Stavros Sachtouris
                raiseCLIError(e, 'KeyValueArgument Syntax Error')
439 f3e94e06 Stavros Sachtouris
440 fd1f1d96 Stavros Sachtouris
441 c3d42104 Stavros Sachtouris
class StatusArgument(ValueArgument):
442 c3d42104 Stavros Sachtouris
    """Initialize with valid_states=['list', 'of', 'states']
443 c3d42104 Stavros Sachtouris
    First state is the default"""
444 c3d42104 Stavros Sachtouris
445 c3d42104 Stavros Sachtouris
    def __init__(self, *args, **kwargs):
446 c3d42104 Stavros Sachtouris
        self.valid_states = kwargs.pop('valid_states', ['BUILD', ])
447 c3d42104 Stavros Sachtouris
        super(StatusArgument, self).__init__(*args, **kwargs)
448 c3d42104 Stavros Sachtouris
449 c3d42104 Stavros Sachtouris
    @property
450 c3d42104 Stavros Sachtouris
    def value(self):
451 c3d42104 Stavros Sachtouris
        return getattr(self, '_value', None)
452 c3d42104 Stavros Sachtouris
453 c3d42104 Stavros Sachtouris
    @value.setter
454 c3d42104 Stavros Sachtouris
    def value(self, new_status):
455 c3d42104 Stavros Sachtouris
        if new_status:
456 c3d42104 Stavros Sachtouris
            new_status = new_status.upper()
457 c3d42104 Stavros Sachtouris
            if new_status not in self.valid_states:
458 c3d42104 Stavros Sachtouris
                raise CLIInvalidArgument(
459 c3d42104 Stavros Sachtouris
                    'Invalid argument %s' % new_status, details=[
460 c3d42104 Stavros Sachtouris
                    'Usage: '
461 c3d42104 Stavros Sachtouris
                    '%s=[%s]' % (self.lvalue, '|'.join(self.valid_states))])
462 c3d42104 Stavros Sachtouris
            self._value = new_status
463 c3d42104 Stavros Sachtouris
464 c3d42104 Stavros Sachtouris
465 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
466 439926dd Stavros Sachtouris
    """Manage a progress bar"""
467 fd1f1d96 Stavros Sachtouris
468 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
469 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
470 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
471 fd1f1d96 Stavros Sachtouris
472 852a22e7 Stavros Sachtouris
    def clone(self):
473 439926dd Stavros Sachtouris
        """Get a modifiable copy of this bar"""
474 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
475 c0fbf04c Stavros Sachtouris
            self.help, self.parsed_name, self.default)
476 852a22e7 Stavros Sachtouris
        newarg._value = self._value
477 852a22e7 Stavros Sachtouris
        return newarg
478 852a22e7 Stavros Sachtouris
479 8547cd19 Stavros Sachtouris
    def get_generator(
480 8547cd19 Stavros Sachtouris
            self, message, message_len=25, countdown=False, timeout=100):
481 439926dd Stavros Sachtouris
        """Get a generator to handle progress of the bar (gen.next())"""
482 fd1f1d96 Stavros Sachtouris
        if self.value:
483 fd1f1d96 Stavros Sachtouris
            return None
484 fd1f1d96 Stavros Sachtouris
        try:
485 1df445fe Stavros Sachtouris
            self.bar = KamakiProgressBar(
486 1df445fe Stavros Sachtouris
                message.ljust(message_len), max=timeout or 100)
487 fd1f1d96 Stavros Sachtouris
        except NameError:
488 329753ae Stavros Sachtouris
            self.value = None
489 329753ae Stavros Sachtouris
            return self.value
490 8547cd19 Stavros Sachtouris
        if countdown:
491 e9c73313 Stavros Sachtouris
            bar_phases = list(self.bar.phases)
492 8547cd19 Stavros Sachtouris
            self.bar.empty_fill, bar_phases[0] = bar_phases[-1], ''
493 8547cd19 Stavros Sachtouris
            bar_phases.reverse()
494 e9c73313 Stavros Sachtouris
            self.bar.phases = bar_phases
495 8547cd19 Stavros Sachtouris
            self.bar.bar_prefix = ' '
496 e9c73313 Stavros Sachtouris
            self.bar.bar_suffix = ' '
497 8547cd19 Stavros Sachtouris
            self.bar.suffix = '%(remaining)ds to timeout'
498 e9c73313 Stavros Sachtouris
        else:
499 e9c73313 Stavros Sachtouris
            self.bar.suffix = '%(percent)d%% - %(eta)ds'
500 a10f5561 Stavros Sachtouris
        self.bar.start()
501 fd1f1d96 Stavros Sachtouris
502 852a22e7 Stavros Sachtouris
        def progress_gen(n):
503 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
504 852a22e7 Stavros Sachtouris
                yield
505 852a22e7 Stavros Sachtouris
            yield
506 852a22e7 Stavros Sachtouris
        return progress_gen
507 fd1f1d96 Stavros Sachtouris
508 852a22e7 Stavros Sachtouris
    def finish(self):
509 439926dd Stavros Sachtouris
        """Stop progress bar, return terminal cursor to user"""
510 852a22e7 Stavros Sachtouris
        if self.value:
511 852a22e7 Stavros Sachtouris
            return
512 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
513 852a22e7 Stavros Sachtouris
        if mybar:
514 852a22e7 Stavros Sachtouris
            mybar.finish()
515 fd1f1d96 Stavros Sachtouris
516 fd1f1d96 Stavros Sachtouris
517 de73876b Stavros Sachtouris
_arguments = dict(
518 de73876b Stavros Sachtouris
    config=_config_arg,
519 144b3551 Stavros Sachtouris
    cloud=ValueArgument('Chose a cloud to connect to', ('--cloud')),
520 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
521 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
522 f6822a26 Stavros Sachtouris
    #include=FlagArgument(
523 f6822a26 Stavros Sachtouris
    #    'Include raw connection data in the output', ('-i', '--include')),
524 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
525 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
526 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
527 1bd4f765 Stavros Sachtouris
    options=RuntimeConfigArgument(
528 cb4a5d9c Stavros Sachtouris
        _config_arg, 'Override a config value', ('-o', '--options'))
529 9bdc89da Stavros Sachtouris
)
530 cb4a5d9c Stavros Sachtouris
531 cb4a5d9c Stavros Sachtouris
532 cb4a5d9c Stavros Sachtouris
#  Initial command line interface arguments
533 439926dd Stavros Sachtouris
534 439926dd Stavros Sachtouris
535 7c2247a0 Stavros Sachtouris
class ArgumentParseManager(object):
536 e0da0f90 Stavros Sachtouris
    """Manage (initialize and update) an ArgumentParser object"""
537 e0da0f90 Stavros Sachtouris
538 49e85ee2 Stavros Sachtouris
    def __init__(
539 49e85ee2 Stavros Sachtouris
            self, exe,
540 9b3c8fd9 Stavros Sachtouris
            arguments=None, required=None, syntax=None, description=None,
541 9b3c8fd9 Stavros Sachtouris
            check_required=True):
542 e0da0f90 Stavros Sachtouris
        """
543 e0da0f90 Stavros Sachtouris
        :param exe: (str) the basic command (e.g. 'kamaki')
544 e0da0f90 Stavros Sachtouris

545 e0da0f90 Stavros Sachtouris
        :param arguments: (dict) if given, overrides the global _argument as
546 e0da0f90 Stavros Sachtouris
            the parsers arguments specification
547 56d84a4e Stavros Sachtouris
        :param required: (list or tuple) an iterable of argument keys, denoting
548 56d84a4e Stavros Sachtouris
            which arguments are required. A tuple denoted an AND relation,
549 56d84a4e Stavros Sachtouris
            while a list denotes an OR relation e.g., ['a', 'b'] means that
550 56d84a4e Stavros Sachtouris
            either 'a' or 'b' is required, while ('a', 'b') means that both 'a'
551 56d84a4e Stavros Sachtouris
            and 'b' ar required.
552 56d84a4e Stavros Sachtouris
            Nesting is allowed e.g., ['a', ('b', 'c'), ['d', 'e']] means that
553 56d84a4e Stavros Sachtouris
            this command required either 'a', or both 'b' and 'c', or one of
554 56d84a4e Stavros Sachtouris
            'd', 'e'.
555 56d84a4e Stavros Sachtouris
            Repeated arguments are also allowed e.g., [('a', 'b'), ('a', 'c'),
556 56d84a4e Stavros Sachtouris
            ['b', 'c']] means that the command required either 'a' and 'b' or
557 56d84a4e Stavros Sachtouris
            'a' and 'c' or at least one of 'b', 'c' and could be written as
558 56d84a4e Stavros Sachtouris
            [('a', ['b', 'c']), ['b', 'c']]
559 49e85ee2 Stavros Sachtouris
        :param syntax: (str) The basic syntax of the arguments. Default:
560 49e85ee2 Stavros Sachtouris
            exe <cmd_group> [<cmd_subbroup> ...] <cmd>
561 49e85ee2 Stavros Sachtouris
        :param description: (str) The description of the commands or ''
562 9b3c8fd9 Stavros Sachtouris
        :param check_required: (bool) Set to False inorder not to check for
563 9b3c8fd9 Stavros Sachtouris
            required argument values while parsing
564 e0da0f90 Stavros Sachtouris
        """
565 b33d9050 Stavros Sachtouris
        self.parser = NoAbbrArgumentParser(
566 320aac17 Stavros Sachtouris
            add_help=False, formatter_class=RawDescriptionHelpFormatter)
567 56d84a4e Stavros Sachtouris
        self._exe = exe
568 49e85ee2 Stavros Sachtouris
        self.syntax = syntax or (
569 49e85ee2 Stavros Sachtouris
            '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe)
570 9b3c8fd9 Stavros Sachtouris
        self.required, self.check_required = required, check_required
571 49e85ee2 Stavros Sachtouris
        self.parser.description = description or ''
572 e0da0f90 Stavros Sachtouris
        if arguments:
573 e0da0f90 Stavros Sachtouris
            self.arguments = arguments
574 e0da0f90 Stavros Sachtouris
        else:
575 e0da0f90 Stavros Sachtouris
            global _arguments
576 e0da0f90 Stavros Sachtouris
            self.arguments = _arguments
577 631b7c35 Stavros Sachtouris
        self._parser_modified, self._parsed, self._unparsed = False, None, None
578 7c2247a0 Stavros Sachtouris
        self.parse()
579 e0da0f90 Stavros Sachtouris
580 56d84a4e Stavros Sachtouris
    @staticmethod
581 56d84a4e Stavros Sachtouris
    def required2list(required):
582 56d84a4e Stavros Sachtouris
        if isinstance(required, list) or isinstance(required, tuple):
583 56d84a4e Stavros Sachtouris
            terms = []
584 56d84a4e Stavros Sachtouris
            for r in required:
585 56d84a4e Stavros Sachtouris
                terms.append(ArgumentParseManager.required2list(r))
586 56d84a4e Stavros Sachtouris
            return list(set(terms).union())
587 56d84a4e Stavros Sachtouris
        return required
588 56d84a4e Stavros Sachtouris
589 56d84a4e Stavros Sachtouris
    @staticmethod
590 56d84a4e Stavros Sachtouris
    def required2str(required, arguments, tab=''):
591 56d84a4e Stavros Sachtouris
        if isinstance(required, list):
592 a6d2ad78 Stavros Sachtouris
            return ' %sat least one of the following:\n%s' % (tab, ''.join(
593 56d84a4e Stavros Sachtouris
                [ArgumentParseManager.required2str(
594 56d84a4e Stavros Sachtouris
                    r, arguments, tab + '  ') for r in required]))
595 56d84a4e Stavros Sachtouris
        elif isinstance(required, tuple):
596 a6d2ad78 Stavros Sachtouris
            return ' %sall of the following:\n%s' % (tab, ''.join(
597 56d84a4e Stavros Sachtouris
                [ArgumentParseManager.required2str(
598 56d84a4e Stavros Sachtouris
                    r, arguments, tab + '  ') for r in required]))
599 56d84a4e Stavros Sachtouris
        else:
600 56d84a4e Stavros Sachtouris
            lt_pn, lt_all, arg = 23, 80, arguments[required]
601 56d84a4e Stavros Sachtouris
            tab2 = ' ' * lt_pn
602 56d84a4e Stavros Sachtouris
            ret = '%s%s' % (tab, ', '.join(arg.parsed_name))
603 56d84a4e Stavros Sachtouris
            if arg.arity != 0:
604 56d84a4e Stavros Sachtouris
                ret += ' %s' % required.upper()
605 56d84a4e Stavros Sachtouris
            ret = ('{:<%s}' % lt_pn).format(ret)
606 264a13f7 Stavros Sachtouris
            prefix = ('\n%s' % tab2) if len(ret) > lt_pn else ' '
607 264a13f7 Stavros Sachtouris
            cur = 0
608 56d84a4e Stavros Sachtouris
            while arg.help[cur:]:
609 264a13f7 Stavros Sachtouris
                next = cur + lt_all - lt_pn
610 56d84a4e Stavros Sachtouris
                ret += prefix
611 56d84a4e Stavros Sachtouris
                ret += ('{:<%s}' % (lt_all - lt_pn)).format(arg.help[cur:next])
612 56d84a4e Stavros Sachtouris
                cur, finish = next, '\n%s' % tab2
613 56d84a4e Stavros Sachtouris
            return ret + '\n'
614 56d84a4e Stavros Sachtouris
615 320af781 Stavros Sachtouris
    @staticmethod
616 320af781 Stavros Sachtouris
    def _patch_with_required_args(arguments, required):
617 320af781 Stavros Sachtouris
        if isinstance(required, tuple):
618 320af781 Stavros Sachtouris
            return ' '.join([ArgumentParseManager._patch_with_required_args(
619 320af781 Stavros Sachtouris
                arguments, k) for k in required])
620 320af781 Stavros Sachtouris
        elif isinstance(required, list):
621 320af781 Stavros Sachtouris
            return '< %s >' % ' | '.join([
622 320af781 Stavros Sachtouris
                ArgumentParseManager._patch_with_required_args(
623 320af781 Stavros Sachtouris
                    arguments, k) for k in required])
624 320af781 Stavros Sachtouris
        arg = arguments[required]
625 320af781 Stavros Sachtouris
        return '/'.join(arg.parsed_name) + (
626 320af781 Stavros Sachtouris
            ' %s [...]' % required.upper() if arg.arity < 0 else (
627 320af781 Stavros Sachtouris
                ' %s' % required.upper() if arg.arity else ''))
628 320af781 Stavros Sachtouris
629 56d84a4e Stavros Sachtouris
    def print_help(self, out=stderr):
630 56d84a4e Stavros Sachtouris
        if self.required:
631 56d84a4e Stavros Sachtouris
            tmp_args = dict(self.arguments)
632 56d84a4e Stavros Sachtouris
            for term in self.required2list(self.required):
633 56d84a4e Stavros Sachtouris
                tmp_args.pop(term)
634 56d84a4e Stavros Sachtouris
            tmp_parser = ArgumentParseManager(self._exe, tmp_args)
635 320af781 Stavros Sachtouris
            tmp_parser.syntax = self.syntax + self._patch_with_required_args(
636 320af781 Stavros Sachtouris
                self.arguments, self.required)
637 56d84a4e Stavros Sachtouris
            tmp_parser.parser.description = '%s\n\nrequired arguments:\n%s' % (
638 56d84a4e Stavros Sachtouris
                self.parser.description,
639 56d84a4e Stavros Sachtouris
                self.required2str(self.required, self.arguments))
640 56d84a4e Stavros Sachtouris
            tmp_parser.update_parser()
641 56d84a4e Stavros Sachtouris
            tmp_parser.parser.print_help()
642 56d84a4e Stavros Sachtouris
        else:
643 56d84a4e Stavros Sachtouris
            self.parser.print_help()
644 56d84a4e Stavros Sachtouris
645 e0da0f90 Stavros Sachtouris
    @property
646 e0da0f90 Stavros Sachtouris
    def syntax(self):
647 b3dd8f4b Stavros Sachtouris
        """The command syntax (useful for help messages, descriptions, etc)"""
648 e0da0f90 Stavros Sachtouris
        return self.parser.prog
649 e0da0f90 Stavros Sachtouris
650 e0da0f90 Stavros Sachtouris
    @syntax.setter
651 e0da0f90 Stavros Sachtouris
    def syntax(self, new_syntax):
652 e0da0f90 Stavros Sachtouris
        self.parser.prog = new_syntax
653 e0da0f90 Stavros Sachtouris
654 b3dd8f4b Stavros Sachtouris
    @property
655 b3dd8f4b Stavros Sachtouris
    def arguments(self):
656 631b7c35 Stavros Sachtouris
        """:returns: (dict) arguments the parser should be aware of"""
657 b3dd8f4b Stavros Sachtouris
        return self._arguments
658 b3dd8f4b Stavros Sachtouris
659 b3dd8f4b Stavros Sachtouris
    @arguments.setter
660 b3dd8f4b Stavros Sachtouris
    def arguments(self, new_arguments):
661 631b7c35 Stavros Sachtouris
        assert isinstance(new_arguments, dict), 'Arguments must be in a dict'
662 b3dd8f4b Stavros Sachtouris
        self._arguments = new_arguments
663 b3dd8f4b Stavros Sachtouris
        self.update_parser()
664 b3dd8f4b Stavros Sachtouris
665 7c2247a0 Stavros Sachtouris
    @property
666 b3dd8f4b Stavros Sachtouris
    def parsed(self):
667 7c2247a0 Stavros Sachtouris
        """(Namespace) parser-matched terms"""
668 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
669 b3dd8f4b Stavros Sachtouris
            self.parse()
670 b3dd8f4b Stavros Sachtouris
        return self._parsed
671 b3dd8f4b Stavros Sachtouris
672 b3dd8f4b Stavros Sachtouris
    @property
673 b3dd8f4b Stavros Sachtouris
    def unparsed(self):
674 b3dd8f4b Stavros Sachtouris
        """(list) parser-unmatched terms"""
675 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
676 b3dd8f4b Stavros Sachtouris
            self.parse()
677 b3dd8f4b Stavros Sachtouris
        return self._unparsed
678 b3dd8f4b Stavros Sachtouris
679 e0da0f90 Stavros Sachtouris
    def update_parser(self, arguments=None):
680 e0da0f90 Stavros Sachtouris
        """Load argument specifications to parser
681 e0da0f90 Stavros Sachtouris

682 e0da0f90 Stavros Sachtouris
        :param arguments: if not given, update self.arguments instead
683 e0da0f90 Stavros Sachtouris
        """
684 631b7c35 Stavros Sachtouris
        arguments = arguments or self._arguments
685 e0da0f90 Stavros Sachtouris
686 e0da0f90 Stavros Sachtouris
        for name, arg in arguments.items():
687 e0da0f90 Stavros Sachtouris
            try:
688 e0da0f90 Stavros Sachtouris
                arg.update_parser(self.parser, name)
689 b3dd8f4b Stavros Sachtouris
                self._parser_modified = True
690 e0da0f90 Stavros Sachtouris
            except ArgumentError:
691 e0da0f90 Stavros Sachtouris
                pass
692 e0da0f90 Stavros Sachtouris
693 7c2247a0 Stavros Sachtouris
    def update_arguments(self, new_arguments):
694 7c2247a0 Stavros Sachtouris
        """Add to / update existing arguments
695 7c2247a0 Stavros Sachtouris

696 7c2247a0 Stavros Sachtouris
        :param new_arguments: (dict)
697 7c2247a0 Stavros Sachtouris
        """
698 7c2247a0 Stavros Sachtouris
        if new_arguments:
699 56d84a4e Stavros Sachtouris
            assert isinstance(new_arguments, dict), 'Arguments not in dict !!!'
700 7c2247a0 Stavros Sachtouris
            self._arguments.update(new_arguments)
701 7c2247a0 Stavros Sachtouris
            self.update_parser()
702 7c2247a0 Stavros Sachtouris
703 0b052394 Stavros Sachtouris
    def _parse_required_arguments(self, required, parsed_args):
704 9b3c8fd9 Stavros Sachtouris
        if not (self.check_required and required):
705 0b052394 Stavros Sachtouris
            return True
706 0b052394 Stavros Sachtouris
        if isinstance(required, tuple):
707 0b052394 Stavros Sachtouris
            for item in required:
708 0b052394 Stavros Sachtouris
                if not self._parse_required_arguments(item, parsed_args):
709 0b052394 Stavros Sachtouris
                    return False
710 0b052394 Stavros Sachtouris
            return True
711 05ecf3a3 Stavros Sachtouris
        elif isinstance(required, list):
712 0b052394 Stavros Sachtouris
            for item in required:
713 0b052394 Stavros Sachtouris
                if self._parse_required_arguments(item, parsed_args):
714 0b052394 Stavros Sachtouris
                    return True
715 0b052394 Stavros Sachtouris
            return False
716 0b052394 Stavros Sachtouris
        return required in parsed_args
717 0b052394 Stavros Sachtouris
718 120126f1 Stavros Sachtouris
    def parse(self, new_args=None):
719 320aac17 Stavros Sachtouris
        """Parse user input"""
720 c8e17a67 Stavros Sachtouris
        try:
721 320aac17 Stavros Sachtouris
            pkargs = (new_args,) if new_args else ()
722 320aac17 Stavros Sachtouris
            self._parsed, unparsed = self.parser.parse_known_args(*pkargs)
723 0b052394 Stavros Sachtouris
            parsed_args = [
724 0b052394 Stavros Sachtouris
                k for k, v in vars(self._parsed).items() if v not in (None, )]
725 0b052394 Stavros Sachtouris
            if not self._parse_required_arguments(self.required, parsed_args):
726 56d84a4e Stavros Sachtouris
                self.print_help()
727 0b052394 Stavros Sachtouris
                raise CLISyntaxError('Missing required arguments')
728 c8e17a67 Stavros Sachtouris
        except SystemExit:
729 c8e17a67 Stavros Sachtouris
            raiseCLIError(CLISyntaxError('Argument Syntax Error'))
730 b3dd8f4b Stavros Sachtouris
        for name, arg in self.arguments.items():
731 b3dd8f4b Stavros Sachtouris
            arg.value = getattr(self._parsed, name, arg.default)
732 b3dd8f4b Stavros Sachtouris
        self._unparsed = []
733 b3dd8f4b Stavros Sachtouris
        for term in unparsed:
734 b3dd8f4b Stavros Sachtouris
            self._unparsed += split_input(' \'%s\' ' % term)
735 b3dd8f4b Stavros Sachtouris
        self._parser_modified = False