Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument.py @ c17b8bc0

History | View | Annotate | Download (13.9 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 e0f40c94 Stavros Sachtouris
38 af6de846 Stavros Sachtouris
from argparse import ArgumentParser, ArgumentError
39 dfee2caf Stavros Sachtouris
40 fd1f1d96 Stavros Sachtouris
try:
41 1d329d27 Stavros Sachtouris
    from progress.bar import FillingCirclesBar as KamakiProgressBar
42 1d329d27 Stavros Sachtouris
    #  IncrementalBar
43 fd1f1d96 Stavros Sachtouris
except ImportError:
44 fd1f1d96 Stavros Sachtouris
    # progress not installed - pls, pip install progress
45 fd1f1d96 Stavros Sachtouris
    pass
46 fd1f1d96 Stavros Sachtouris
47 fd5db045 Stavros Sachtouris
48 dfee2caf Stavros Sachtouris
class Argument(object):
49 edb7fc1a Stavros Sachtouris
    """An argument that can be parsed from command line or otherwise.
50 edb7fc1a Stavros Sachtouris
    This is the general Argument class. It is suggested to extent this
51 edb7fc1a Stavros Sachtouris
    class into more specific argument types.
52 edb7fc1a Stavros Sachtouris
    """
53 dfee2caf Stavros Sachtouris
54 dfee2caf Stavros Sachtouris
    def __init__(self, arity, help=None, parsed_name=None, default=None):
55 606fe15f Stavros Sachtouris
        self.arity = int(arity)
56 606fe15f Stavros Sachtouris
57 dfee2caf Stavros Sachtouris
        if help is not None:
58 dfee2caf Stavros Sachtouris
            self.help = help
59 dfee2caf Stavros Sachtouris
        if parsed_name is not None:
60 dfee2caf Stavros Sachtouris
            self.parsed_name = parsed_name
61 dfee2caf Stavros Sachtouris
        if default is not None:
62 dfee2caf Stavros Sachtouris
            self.default = default
63 dfee2caf Stavros Sachtouris
64 fd5db045 Stavros Sachtouris
    @property
65 dfee2caf Stavros Sachtouris
    def parsed_name(self):
66 edb7fc1a Stavros Sachtouris
        """the string which will be recognised by the parser as an instance
67 edb7fc1a Stavros Sachtouris
            of this argument
68 edb7fc1a Stavros Sachtouris
        """
69 dfee2caf Stavros Sachtouris
        return getattr(self, '_parsed_name', None)
70 fd5db045 Stavros Sachtouris
71 dfee2caf Stavros Sachtouris
    @parsed_name.setter
72 dfee2caf Stavros Sachtouris
    def parsed_name(self, newname):
73 dfee2caf Stavros Sachtouris
        self._parsed_name = getattr(self, '_parsed_name', [])
74 dfee2caf Stavros Sachtouris
        if isinstance(newname, list) or isinstance(newname, tuple):
75 dfee2caf Stavros Sachtouris
            self._parsed_name += list(newname)
76 dfee2caf Stavros Sachtouris
        else:
77 dfee2caf Stavros Sachtouris
            self._parsed_name.append(unicode(newname))
78 dfee2caf Stavros Sachtouris
79 fd5db045 Stavros Sachtouris
    @property
80 dfee2caf Stavros Sachtouris
    def help(self):
81 edb7fc1a Stavros Sachtouris
        """a user friendly help message"""
82 dfee2caf Stavros Sachtouris
        return getattr(self, '_help', None)
83 fd5db045 Stavros Sachtouris
84 dfee2caf Stavros Sachtouris
    @help.setter
85 dfee2caf Stavros Sachtouris
    def help(self, newhelp):
86 dfee2caf Stavros Sachtouris
        self._help = unicode(newhelp)
87 dfee2caf Stavros Sachtouris
88 fd5db045 Stavros Sachtouris
    @property
89 dfee2caf Stavros Sachtouris
    def arity(self):
90 edb7fc1a Stavros Sachtouris
        """negative for repeating, 0 for flag, 1 or more for values"""
91 dfee2caf Stavros Sachtouris
        return getattr(self, '_arity', None)
92 fd5db045 Stavros Sachtouris
93 dfee2caf Stavros Sachtouris
    @arity.setter
94 dfee2caf Stavros Sachtouris
    def arity(self, newarity):
95 dfee2caf Stavros Sachtouris
        newarity = int(newarity)
96 dfee2caf Stavros Sachtouris
        self._arity = newarity
97 dfee2caf Stavros Sachtouris
98 fd5db045 Stavros Sachtouris
    @property
99 dfee2caf Stavros Sachtouris
    def default(self):
100 edb7fc1a Stavros Sachtouris
        """the value of this argument when not set"""
101 dfee2caf Stavros Sachtouris
        if not hasattr(self, '_default'):
102 dfee2caf Stavros Sachtouris
            self._default = False if self.arity == 0 else None
103 dfee2caf Stavros Sachtouris
        return self._default
104 fd5db045 Stavros Sachtouris
105 dfee2caf Stavros Sachtouris
    @default.setter
106 dfee2caf Stavros Sachtouris
    def default(self, newdefault):
107 dfee2caf Stavros Sachtouris
        self._default = newdefault
108 dfee2caf Stavros Sachtouris
109 fd5db045 Stavros Sachtouris
    @property
110 dfee2caf Stavros Sachtouris
    def value(self):
111 edb7fc1a Stavros Sachtouris
        """the value of the argument"""
112 dfee2caf Stavros Sachtouris
        return getattr(self, '_value', self.default)
113 fd5db045 Stavros Sachtouris
114 dfee2caf Stavros Sachtouris
    @value.setter
115 dfee2caf Stavros Sachtouris
    def value(self, newvalue):
116 dfee2caf Stavros Sachtouris
        self._value = newvalue
117 dfee2caf Stavros Sachtouris
118 dfee2caf Stavros Sachtouris
    def update_parser(self, parser, name):
119 edb7fc1a Stavros Sachtouris
        """Update argument parser with self info"""
120 0a0b9fb6 Stavros Sachtouris
        action = 'append' if self.arity < 0\
121 0a0b9fb6 Stavros Sachtouris
            else 'store_true' if self.arity == 0\
122 0a0b9fb6 Stavros Sachtouris
            else 'store'
123 dfee2caf Stavros Sachtouris
        parser.add_argument(*self.parsed_name, dest=name, action=action,
124 dfee2caf Stavros Sachtouris
            default=self.default, help=self.help)
125 dfee2caf Stavros Sachtouris
126 dfee2caf Stavros Sachtouris
    def main(self):
127 edb7fc1a Stavros Sachtouris
        """Overide this method to give functionality to your args"""
128 dfee2caf Stavros Sachtouris
        raise NotImplementedError
129 dfee2caf Stavros Sachtouris
130 fd5db045 Stavros Sachtouris
131 9bdc89da Stavros Sachtouris
class ConfigArgument(Argument):
132 439926dd Stavros Sachtouris
    """Manage a kamaki configuration (file)"""
133 edb7fc1a Stavros Sachtouris
134 e9a92550 Stavros Sachtouris
    _config_file = None
135 e9a92550 Stavros Sachtouris
136 fd5db045 Stavros Sachtouris
    @property
137 c41a86b2 Stavros Sachtouris
    def value(self):
138 439926dd Stavros Sachtouris
        """A Config object"""
139 d486baec Stavros Sachtouris
        super(self.__class__, self).value
140 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
141 fd5db045 Stavros Sachtouris
142 c41a86b2 Stavros Sachtouris
    @value.setter
143 c41a86b2 Stavros Sachtouris
    def value(self, config_file):
144 e9a92550 Stavros Sachtouris
        if config_file:
145 e9a92550 Stavros Sachtouris
            self._value = Config(config_file)
146 e9a92550 Stavros Sachtouris
            self._config_file = config_file
147 e9a92550 Stavros Sachtouris
        elif self._config_file:
148 e9a92550 Stavros Sachtouris
            self._value = Config(self._config_file)
149 e9a92550 Stavros Sachtouris
        else:
150 e9a92550 Stavros Sachtouris
            self._value = Config()
151 9bdc89da Stavros Sachtouris
152 c41a86b2 Stavros Sachtouris
    def get(self, group, term):
153 439926dd Stavros Sachtouris
        """Get a configuration setting from the Config object"""
154 c41a86b2 Stavros Sachtouris
        return self.value.get(group, term)
155 c41a86b2 Stavros Sachtouris
156 c41a86b2 Stavros Sachtouris
    def get_groups(self):
157 c41a86b2 Stavros Sachtouris
        return self.value.apis()
158 017d37ce Stavros Sachtouris
159 017d37ce Stavros Sachtouris
_config_arg = ConfigArgument(1, 'Path to configuration file', '--config')
160 017d37ce Stavros Sachtouris
161 fd5db045 Stavros Sachtouris
162 9bdc89da Stavros Sachtouris
class CmdLineConfigArgument(Argument):
163 edb7fc1a Stavros Sachtouris
    """Set a run-time setting option (not persistent)"""
164 edb7fc1a Stavros Sachtouris
165 c41a86b2 Stavros Sachtouris
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
166 c41a86b2 Stavros Sachtouris
        super(self.__class__, self).__init__(1, help, parsed_name, default)
167 c41a86b2 Stavros Sachtouris
        self._config_arg = config_arg
168 c41a86b2 Stavros Sachtouris
169 fd5db045 Stavros Sachtouris
    @property
170 c41a86b2 Stavros Sachtouris
    def value(self):
171 439926dd Stavros Sachtouris
        """A key=val option"""
172 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
173 fd5db045 Stavros Sachtouris
174 c41a86b2 Stavros Sachtouris
    @value.setter
175 c41a86b2 Stavros Sachtouris
    def value(self, options):
176 c41a86b2 Stavros Sachtouris
        if options == self.default:
177 c41a86b2 Stavros Sachtouris
            return
178 fd5db045 Stavros Sachtouris
        if not isinstance(options, list):
179 fd5db045 Stavros Sachtouris
            options = [unicode(options)]
180 c41a86b2 Stavros Sachtouris
        for option in options:
181 c41a86b2 Stavros Sachtouris
            keypath, sep, val = option.partition('=')
182 c41a86b2 Stavros Sachtouris
            if not sep:
183 b696ed2c Stavros Sachtouris
                raiseCLIError(CLISyntaxError('Argument Syntax Error '),
184 b696ed2c Stavros Sachtouris
                    details=['%s is missing a "="',
185 b696ed2c Stavros Sachtouris
                    ' (usage: -o section.key=val)' % option]
186 b696ed2c Stavros Sachtouris
                )
187 c41a86b2 Stavros Sachtouris
            section, sep, key = keypath.partition('.')
188 0a0b9fb6 Stavros Sachtouris
        if not sep:
189 0a0b9fb6 Stavros Sachtouris
            key = section
190 0a0b9fb6 Stavros Sachtouris
            section = 'global'
191 0a0b9fb6 Stavros Sachtouris
        self._config_arg.value.override(
192 0a0b9fb6 Stavros Sachtouris
            section.strip(),
193 fd5db045 Stavros Sachtouris
            key.strip(),
194 fd5db045 Stavros Sachtouris
            val.strip())
195 fd5db045 Stavros Sachtouris
196 c41a86b2 Stavros Sachtouris
197 c41a86b2 Stavros Sachtouris
class FlagArgument(Argument):
198 edb7fc1a Stavros Sachtouris
    """
199 439926dd Stavros Sachtouris
    :value: true if set, false otherwise
200 edb7fc1a Stavros Sachtouris
    """
201 edb7fc1a Stavros Sachtouris
202 40a9c357 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=False):
203 c41a86b2 Stavros Sachtouris
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
204 c41a86b2 Stavros Sachtouris
205 fd5db045 Stavros Sachtouris
206 c41a86b2 Stavros Sachtouris
class ValueArgument(Argument):
207 edb7fc1a Stavros Sachtouris
    """
208 edb7fc1a Stavros Sachtouris
    :value type: string
209 edb7fc1a Stavros Sachtouris
    :value returns: given value or default
210 edb7fc1a Stavros Sachtouris
    """
211 edb7fc1a Stavros Sachtouris
212 c41a86b2 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=None):
213 c41a86b2 Stavros Sachtouris
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
214 c41a86b2 Stavros Sachtouris
215 fd5db045 Stavros Sachtouris
216 e3d4d442 Stavros Sachtouris
class IntArgument(ValueArgument):
217 edb7fc1a Stavros Sachtouris
218 fd5db045 Stavros Sachtouris
    @property
219 e3d4d442 Stavros Sachtouris
    def value(self):
220 439926dd Stavros Sachtouris
        """integer (type checking)"""
221 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
222 fd5db045 Stavros Sachtouris
223 e3d4d442 Stavros Sachtouris
    @value.setter
224 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
225 e3d4d442 Stavros Sachtouris
        if newvalue == self.default:
226 e3d4d442 Stavros Sachtouris
            self._value = self.default
227 e3d4d442 Stavros Sachtouris
            return
228 e3d4d442 Stavros Sachtouris
        try:
229 e3d4d442 Stavros Sachtouris
            self._value = int(newvalue)
230 e3d4d442 Stavros Sachtouris
        except ValueError:
231 b696ed2c Stavros Sachtouris
            raiseCLIError(CLISyntaxError('IntArgument Error'),
232 fd5db045 Stavros Sachtouris
                details='Value %s not an int' % newvalue)
233 fd5db045 Stavros Sachtouris
234 e3d4d442 Stavros Sachtouris
235 c41a86b2 Stavros Sachtouris
class VersionArgument(FlagArgument):
236 edb7fc1a Stavros Sachtouris
    """A flag argument with that prints current version"""
237 edb7fc1a Stavros Sachtouris
238 fd5db045 Stavros Sachtouris
    @property
239 c41a86b2 Stavros Sachtouris
    def value(self):
240 439926dd Stavros Sachtouris
        """bool"""
241 c41a86b2 Stavros Sachtouris
        return super(self.__class__, self).value
242 fd5db045 Stavros Sachtouris
243 c41a86b2 Stavros Sachtouris
    @value.setter
244 c41a86b2 Stavros Sachtouris
    def value(self, newvalue):
245 c41a86b2 Stavros Sachtouris
        self._value = newvalue
246 c41a86b2 Stavros Sachtouris
        self.main()
247 c41a86b2 Stavros Sachtouris
248 c41a86b2 Stavros Sachtouris
    def main(self):
249 439926dd Stavros Sachtouris
        """Print current version"""
250 c41a86b2 Stavros Sachtouris
        if self.value:
251 c41a86b2 Stavros Sachtouris
            import kamaki
252 fd5db045 Stavros Sachtouris
            print('kamaki %s' % kamaki.__version__)
253 fd5db045 Stavros Sachtouris
254 9bdc89da Stavros Sachtouris
255 0a0b9fb6 Stavros Sachtouris
class KeyValueArgument(Argument):
256 edb7fc1a Stavros Sachtouris
    """A Value Argument that can be repeated
257 edb7fc1a Stavros Sachtouris

258 439926dd Stavros Sachtouris
    :syntax: --<arg> key1=value1 --<arg> key2=value2 ...
259 edb7fc1a Stavros Sachtouris
    """
260 edb7fc1a Stavros Sachtouris
261 0a0b9fb6 Stavros Sachtouris
    def __init__(self, help='', parsed_name=None, default=[]):
262 0a0b9fb6 Stavros Sachtouris
        super(KeyValueArgument, self).__init__(-1, help, parsed_name, default)
263 f3e94e06 Stavros Sachtouris
264 fd5db045 Stavros Sachtouris
    @property
265 f3e94e06 Stavros Sachtouris
    def value(self):
266 439926dd Stavros Sachtouris
        """
267 439926dd Stavros Sachtouris
        :input: key=value
268 439926dd Stavros Sachtouris
        :output: {'key1':'value1', 'key2':'value2', ...}
269 439926dd Stavros Sachtouris
        """
270 f3e94e06 Stavros Sachtouris
        return super(KeyValueArgument, self).value
271 fd5db045 Stavros Sachtouris
272 fd5db045 Stavros Sachtouris
    @value.setter
273 f3e94e06 Stavros Sachtouris
    def value(self, keyvalue_pairs):
274 0a0b9fb6 Stavros Sachtouris
        self._value = {}
275 f3e94e06 Stavros Sachtouris
        for pair in keyvalue_pairs:
276 fd5db045 Stavros Sachtouris
            key, sep, val = pair.partition('=')
277 f3e94e06 Stavros Sachtouris
            if not sep:
278 b696ed2c Stavros Sachtouris
                raiseCLIError(CLISyntaxError('Argument syntax error '),
279 0a0b9fb6 Stavros Sachtouris
                    details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
280 23803b28 Stavros Sachtouris
            self._value[key.strip()] = val.strip()
281 f3e94e06 Stavros Sachtouris
282 fd1f1d96 Stavros Sachtouris
283 fd1f1d96 Stavros Sachtouris
class ProgressBarArgument(FlagArgument):
284 439926dd Stavros Sachtouris
    """Manage a progress bar"""
285 fd1f1d96 Stavros Sachtouris
286 fd1f1d96 Stavros Sachtouris
    def __init__(self, help='', parsed_name='', default=True):
287 fd1f1d96 Stavros Sachtouris
        self.suffix = '%(percent)d%%'
288 fd1f1d96 Stavros Sachtouris
        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
289 fd1f1d96 Stavros Sachtouris
        try:
290 1d329d27 Stavros Sachtouris
            KamakiProgressBar
291 fd1f1d96 Stavros Sachtouris
        except NameError:
292 439926dd Stavros Sachtouris
            print('Warning: no progress bar functionality')
293 fd1f1d96 Stavros Sachtouris
294 852a22e7 Stavros Sachtouris
    def clone(self):
295 439926dd Stavros Sachtouris
        """Get a modifiable copy of this bar"""
296 852a22e7 Stavros Sachtouris
        newarg = ProgressBarArgument(
297 852a22e7 Stavros Sachtouris
            self.help,
298 852a22e7 Stavros Sachtouris
            self.parsed_name,
299 852a22e7 Stavros Sachtouris
            self.default)
300 852a22e7 Stavros Sachtouris
        newarg._value = self._value
301 852a22e7 Stavros Sachtouris
        return newarg
302 852a22e7 Stavros Sachtouris
303 fd1f1d96 Stavros Sachtouris
    def get_generator(self, message, message_len=25):
304 439926dd Stavros Sachtouris
        """Get a generator to handle progress of the bar (gen.next())"""
305 fd1f1d96 Stavros Sachtouris
        if self.value:
306 fd1f1d96 Stavros Sachtouris
            return None
307 fd1f1d96 Stavros Sachtouris
        try:
308 1d329d27 Stavros Sachtouris
            self.bar = KamakiProgressBar()
309 fd1f1d96 Stavros Sachtouris
        except NameError:
310 329753ae Stavros Sachtouris
            self.value = None
311 329753ae Stavros Sachtouris
            return self.value
312 329753ae Stavros Sachtouris
        self.bar.message = message.ljust(message_len)
313 852a22e7 Stavros Sachtouris
        self.bar.suffix = '%(percent)d%% - %(eta)ds'
314 a10f5561 Stavros Sachtouris
        self.bar.start()
315 fd1f1d96 Stavros Sachtouris
316 852a22e7 Stavros Sachtouris
        def progress_gen(n):
317 852a22e7 Stavros Sachtouris
            for i in self.bar.iter(range(int(n))):
318 852a22e7 Stavros Sachtouris
                yield
319 852a22e7 Stavros Sachtouris
            yield
320 852a22e7 Stavros Sachtouris
        return progress_gen
321 fd1f1d96 Stavros Sachtouris
322 852a22e7 Stavros Sachtouris
    def finish(self):
323 439926dd Stavros Sachtouris
        """Stop progress bar, return terminal cursor to user"""
324 852a22e7 Stavros Sachtouris
        if self.value:
325 852a22e7 Stavros Sachtouris
            return
326 852a22e7 Stavros Sachtouris
        mybar = getattr(self, 'bar', None)
327 852a22e7 Stavros Sachtouris
        if mybar:
328 852a22e7 Stavros Sachtouris
            mybar.finish()
329 fd1f1d96 Stavros Sachtouris
330 fd1f1d96 Stavros Sachtouris
331 fd5db045 Stavros Sachtouris
_arguments = dict(config=_config_arg,
332 fd5db045 Stavros Sachtouris
    help=Argument(0, 'Show help message', ('-h', '--help')),
333 fd5db045 Stavros Sachtouris
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
334 fd5db045 Stavros Sachtouris
    include=FlagArgument('Include protocol headers in the output',
335 fd5db045 Stavros Sachtouris
        ('-i', '--include')),
336 fd5db045 Stavros Sachtouris
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
337 fd5db045 Stavros Sachtouris
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
338 fd5db045 Stavros Sachtouris
    version=VersionArgument('Print current version', ('-V', '--version')),
339 fd5db045 Stavros Sachtouris
    options=CmdLineConfigArgument(_config_arg,
340 fd5db045 Stavros Sachtouris
        'Override a config value',
341 fd5db045 Stavros Sachtouris
        ('-o', '--options'))
342 9bdc89da Stavros Sachtouris
)
343 439926dd Stavros Sachtouris
"""Initial command line interface arguments"""
344 439926dd Stavros Sachtouris
345 439926dd Stavros Sachtouris
346 439926dd Stavros Sachtouris
"""
347 439926dd Stavros Sachtouris
Mechanism:
348 439926dd Stavros Sachtouris
    init_parser
349 439926dd Stavros Sachtouris
    parse_known_args
350 439926dd Stavros Sachtouris
    manage top-level user arguments input
351 439926dd Stavros Sachtouris
    find user-requested command
352 439926dd Stavros Sachtouris
    add command-specific arguments to dict
353 439926dd Stavros Sachtouris
    update_arguments
354 439926dd Stavros Sachtouris
"""
355 439926dd Stavros Sachtouris
356 439926dd Stavros Sachtouris
357 7c2247a0 Stavros Sachtouris
class ArgumentParseManager(object):
358 e0da0f90 Stavros Sachtouris
    """Manage (initialize and update) an ArgumentParser object"""
359 e0da0f90 Stavros Sachtouris
360 5ad77121 Stavros Sachtouris
    parser = None
361 7c2247a0 Stavros Sachtouris
    _arguments = {}
362 b3dd8f4b Stavros Sachtouris
    _parser_modified = False
363 b3dd8f4b Stavros Sachtouris
    _parsed = None
364 b3dd8f4b Stavros Sachtouris
    _unparsed = None
365 e0da0f90 Stavros Sachtouris
366 e0da0f90 Stavros Sachtouris
    def __init__(self, exe, arguments=None):
367 e0da0f90 Stavros Sachtouris
        """
368 e0da0f90 Stavros Sachtouris
        :param exe: (str) the basic command (e.g. 'kamaki')
369 e0da0f90 Stavros Sachtouris

370 e0da0f90 Stavros Sachtouris
        :param arguments: (dict) if given, overrides the global _argument as
371 e0da0f90 Stavros Sachtouris
            the parsers arguments specification
372 e0da0f90 Stavros Sachtouris
        """
373 5ad77121 Stavros Sachtouris
        self.parser = ArgumentParser(add_help=False)
374 b3dd8f4b Stavros Sachtouris
        self.syntax = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe
375 e0da0f90 Stavros Sachtouris
        if arguments:
376 e0da0f90 Stavros Sachtouris
            self.arguments = arguments
377 e0da0f90 Stavros Sachtouris
        else:
378 e0da0f90 Stavros Sachtouris
            global _arguments
379 e0da0f90 Stavros Sachtouris
            self.arguments = _arguments
380 7c2247a0 Stavros Sachtouris
        self.parse()
381 e0da0f90 Stavros Sachtouris
382 e0da0f90 Stavros Sachtouris
    @property
383 e0da0f90 Stavros Sachtouris
    def syntax(self):
384 b3dd8f4b Stavros Sachtouris
        """The command syntax (useful for help messages, descriptions, etc)"""
385 e0da0f90 Stavros Sachtouris
        return self.parser.prog
386 e0da0f90 Stavros Sachtouris
387 e0da0f90 Stavros Sachtouris
    @syntax.setter
388 e0da0f90 Stavros Sachtouris
    def syntax(self, new_syntax):
389 e0da0f90 Stavros Sachtouris
        self.parser.prog = new_syntax
390 e0da0f90 Stavros Sachtouris
391 b3dd8f4b Stavros Sachtouris
    @property
392 b3dd8f4b Stavros Sachtouris
    def arguments(self):
393 b3dd8f4b Stavros Sachtouris
        """(dict) arguments the parser should be aware of"""
394 b3dd8f4b Stavros Sachtouris
        return self._arguments
395 b3dd8f4b Stavros Sachtouris
396 b3dd8f4b Stavros Sachtouris
    @arguments.setter
397 b3dd8f4b Stavros Sachtouris
    def arguments(self, new_arguments):
398 b3dd8f4b Stavros Sachtouris
        if new_arguments:
399 b3dd8f4b Stavros Sachtouris
            assert isinstance(new_arguments, dict)
400 b3dd8f4b Stavros Sachtouris
        self._arguments = new_arguments
401 b3dd8f4b Stavros Sachtouris
        self.update_parser()
402 b3dd8f4b Stavros Sachtouris
403 7c2247a0 Stavros Sachtouris
    @property
404 b3dd8f4b Stavros Sachtouris
    def parsed(self):
405 7c2247a0 Stavros Sachtouris
        """(Namespace) parser-matched terms"""
406 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
407 b3dd8f4b Stavros Sachtouris
            self.parse()
408 b3dd8f4b Stavros Sachtouris
        return self._parsed
409 b3dd8f4b Stavros Sachtouris
410 b3dd8f4b Stavros Sachtouris
    @property
411 b3dd8f4b Stavros Sachtouris
    def unparsed(self):
412 b3dd8f4b Stavros Sachtouris
        """(list) parser-unmatched terms"""
413 b3dd8f4b Stavros Sachtouris
        if self._parser_modified:
414 b3dd8f4b Stavros Sachtouris
            self.parse()
415 b3dd8f4b Stavros Sachtouris
        return self._unparsed
416 b3dd8f4b Stavros Sachtouris
417 e0da0f90 Stavros Sachtouris
    def update_parser(self, arguments=None):
418 e0da0f90 Stavros Sachtouris
        """Load argument specifications to parser
419 e0da0f90 Stavros Sachtouris

420 e0da0f90 Stavros Sachtouris
        :param arguments: if not given, update self.arguments instead
421 e0da0f90 Stavros Sachtouris
        """
422 e0da0f90 Stavros Sachtouris
        if not arguments:
423 b3dd8f4b Stavros Sachtouris
            arguments = self._arguments
424 e0da0f90 Stavros Sachtouris
425 e0da0f90 Stavros Sachtouris
        for name, arg in arguments.items():
426 e0da0f90 Stavros Sachtouris
            try:
427 e0da0f90 Stavros Sachtouris
                arg.update_parser(self.parser, name)
428 b3dd8f4b Stavros Sachtouris
                self._parser_modified = True
429 e0da0f90 Stavros Sachtouris
            except ArgumentError:
430 e0da0f90 Stavros Sachtouris
                pass
431 e0da0f90 Stavros Sachtouris
432 7c2247a0 Stavros Sachtouris
    def update_arguments(self, new_arguments):
433 7c2247a0 Stavros Sachtouris
        """Add to / update existing arguments
434 7c2247a0 Stavros Sachtouris

435 7c2247a0 Stavros Sachtouris
        :param new_arguments: (dict)
436 7c2247a0 Stavros Sachtouris
        """
437 7c2247a0 Stavros Sachtouris
        if new_arguments:
438 7c2247a0 Stavros Sachtouris
            assert isinstance(new_arguments, dict)
439 7c2247a0 Stavros Sachtouris
            self._arguments.update(new_arguments)
440 7c2247a0 Stavros Sachtouris
            self.update_parser()
441 7c2247a0 Stavros Sachtouris
442 120126f1 Stavros Sachtouris
    def parse(self, new_args=None):
443 7c2247a0 Stavros Sachtouris
        """Do parse user input"""
444 120126f1 Stavros Sachtouris
        if new_args:
445 120126f1 Stavros Sachtouris
            self._parsed, unparsed = self.parser.parse_known_args(new_args)
446 120126f1 Stavros Sachtouris
        else:
447 120126f1 Stavros Sachtouris
            self._parsed, unparsed = self.parser.parse_known_args()
448 b3dd8f4b Stavros Sachtouris
        for name, arg in self.arguments.items():
449 b3dd8f4b Stavros Sachtouris
            arg.value = getattr(self._parsed, name, arg.default)
450 b3dd8f4b Stavros Sachtouris
        self._unparsed = []
451 b3dd8f4b Stavros Sachtouris
        for term in unparsed:
452 b3dd8f4b Stavros Sachtouris
            self._unparsed += split_input(' \'%s\' ' % term)
453 b3dd8f4b Stavros Sachtouris
        self._parser_modified = False
454 b3dd8f4b Stavros Sachtouris
455 e0da0f90 Stavros Sachtouris
456 af6de846 Stavros Sachtouris
def update_arguments(parser, arguments):
457 439926dd Stavros Sachtouris
    """Update arguments dict from user input
458 439926dd Stavros Sachtouris

459 439926dd Stavros Sachtouris
    """
460 af6de846 Stavros Sachtouris
    for name, argument in arguments.items():
461 af6de846 Stavros Sachtouris
        try:
462 af6de846 Stavros Sachtouris
            argument.update_parser(parser, name)
463 af6de846 Stavros Sachtouris
        except ArgumentError:
464 af6de846 Stavros Sachtouris
            pass