root / kamaki / cli / argument / __init__.py @ 58602137
History | View | Annotate | Download (15.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 | b696ed2c | Stavros Sachtouris | from kamaki.cli.errors import CLISyntaxError, raiseCLIError |
36 | e0f40c94 | Stavros Sachtouris | from kamaki.cli.utils import split_input |
37 | 7637d600 | Stavros Sachtouris | |
38 | 04d01cd4 | Stavros Sachtouris | from datetime import datetime as dtm |
39 | ea4a21b8 | Stavros Sachtouris | from time import mktime |
40 | db8d1766 | Stavros Sachtouris | |
41 | 7637d600 | Stavros Sachtouris | from logging import getLogger |
42 | af6de846 | Stavros Sachtouris | from argparse import ArgumentParser, ArgumentError |
43 | 2703cceb | Stavros Sachtouris | from argparse import RawDescriptionHelpFormatter |
44 | 67083ca0 | Stavros Sachtouris | from progress.bar import ShadyBar as KamakiProgressBar |
45 | fd1f1d96 | Stavros Sachtouris | |
46 | 7637d600 | Stavros Sachtouris | log = getLogger(__name__) |
47 | db8d1766 | Stavros Sachtouris | |
48 | fd5db045 | Stavros Sachtouris | |
49 | dfee2caf | Stavros Sachtouris | class Argument(object): |
50 | edb7fc1a | Stavros Sachtouris | """An argument that can be parsed from command line or otherwise.
|
51 | 5286e2c3 | Stavros Sachtouris | This is the top-level Argument class. It is suggested to extent this
|
52 | edb7fc1a | Stavros Sachtouris | class into more specific argument types.
|
53 | edb7fc1a | Stavros Sachtouris | """
|
54 | dfee2caf | Stavros Sachtouris | |
55 | dfee2caf | Stavros Sachtouris | def __init__(self, arity, help=None, parsed_name=None, default=None): |
56 | 606fe15f | Stavros Sachtouris | self.arity = int(arity) |
57 | 5286e2c3 | Stavros Sachtouris | self.help = '%s' % help or '' |
58 | dfee2caf | Stavros Sachtouris | |
59 | f17d6cb5 | Stavros Sachtouris | assert parsed_name, 'No parsed name for argument %s' % self |
60 | f17d6cb5 | Stavros Sachtouris | self.parsed_name = list(parsed_name) if isinstance( |
61 | f17d6cb5 | Stavros Sachtouris | parsed_name, list) or isinstance(parsed_name, tuple) else ( |
62 | f17d6cb5 | Stavros Sachtouris | '%s' % parsed_name).split()
|
63 | f17d6cb5 | Stavros Sachtouris | for name in self.parsed_name: |
64 | f17d6cb5 | Stavros Sachtouris | assert name.count(' ') == 0, '%s: Invalid parse name "%s"' % ( |
65 | f17d6cb5 | Stavros Sachtouris | self, name)
|
66 | f17d6cb5 | Stavros Sachtouris | msg = '%s: Invalid parse name "%s" should start with a "-"' % (
|
67 | f17d6cb5 | Stavros Sachtouris | self, name)
|
68 | f17d6cb5 | Stavros Sachtouris | assert name.startswith('-'), msg |
69 | f17d6cb5 | Stavros Sachtouris | |
70 | f52b39db | Stavros Sachtouris | self.default = default if (default or self.arity) else False |
71 | dfee2caf | Stavros Sachtouris | |
72 | fd5db045 | Stavros Sachtouris | @property
|
73 | dfee2caf | Stavros Sachtouris | def value(self): |
74 | dfee2caf | Stavros Sachtouris | return getattr(self, '_value', self.default) |
75 | fd5db045 | Stavros Sachtouris | |
76 | dfee2caf | Stavros Sachtouris | @value.setter
|
77 | dfee2caf | Stavros Sachtouris | def value(self, newvalue): |
78 | dfee2caf | Stavros Sachtouris | self._value = newvalue
|
79 | dfee2caf | Stavros Sachtouris | |
80 | dfee2caf | Stavros Sachtouris | def update_parser(self, parser, name): |
81 | edb7fc1a | Stavros Sachtouris | """Update argument parser with self info"""
|
82 | f17d6cb5 | Stavros Sachtouris | action = 'append' if self.arity < 0 else ( |
83 | f17d6cb5 | Stavros Sachtouris | 'store' if self.arity else 'store_true') |
84 | de73876b | Stavros Sachtouris | parser.add_argument( |
85 | de73876b | Stavros Sachtouris | *self.parsed_name,
|
86 | f17d6cb5 | Stavros Sachtouris | dest=name, action=action, default=self.default, help=self.help) |
87 | dfee2caf | Stavros Sachtouris | |
88 | fd5db045 | Stavros Sachtouris | |
89 | 9bdc89da | Stavros Sachtouris | class ConfigArgument(Argument): |
90 | 439926dd | Stavros Sachtouris | """Manage a kamaki configuration (file)"""
|
91 | edb7fc1a | Stavros Sachtouris | |
92 | a7aacf12 | Stavros Sachtouris | def __init__(self, help, parsed_name=('-c', '--config')): |
93 | a7aacf12 | Stavros Sachtouris | super(ConfigArgument, self).__init__(1, help, parsed_name, None) |
94 | a7aacf12 | Stavros Sachtouris | self.file_path = None |
95 | e9a92550 | Stavros Sachtouris | |
96 | fd5db045 | Stavros Sachtouris | @property
|
97 | c41a86b2 | Stavros Sachtouris | def value(self): |
98 | a7aacf12 | Stavros Sachtouris | return super(ConfigArgument, self).value |
99 | fd5db045 | Stavros Sachtouris | |
100 | c41a86b2 | Stavros Sachtouris | @value.setter
|
101 | c41a86b2 | Stavros Sachtouris | def value(self, config_file): |
102 | e9a92550 | Stavros Sachtouris | if config_file:
|
103 | e9a92550 | Stavros Sachtouris | self._value = Config(config_file)
|
104 | a7aacf12 | Stavros Sachtouris | self.file_path = config_file
|
105 | a7aacf12 | Stavros Sachtouris | elif self.file_path: |
106 | a7aacf12 | Stavros Sachtouris | self._value = Config(self.file_path) |
107 | e9a92550 | Stavros Sachtouris | else:
|
108 | e9a92550 | Stavros Sachtouris | self._value = Config()
|
109 | 9bdc89da | Stavros Sachtouris | |
110 | c41a86b2 | Stavros Sachtouris | def get(self, group, term): |
111 | 439926dd | Stavros Sachtouris | """Get a configuration setting from the Config object"""
|
112 | c41a86b2 | Stavros Sachtouris | return self.value.get(group, term) |
113 | c41a86b2 | Stavros Sachtouris | |
114 | a7aacf12 | Stavros Sachtouris | @property
|
115 | a7aacf12 | Stavros Sachtouris | def groups(self): |
116 | 362adf50 | Stavros Sachtouris | suffix = '_cli'
|
117 | 362adf50 | Stavros Sachtouris | slen = len(suffix)
|
118 | 362adf50 | Stavros Sachtouris | return [term[:-slen] for term in self.value.keys('global') if ( |
119 | 362adf50 | Stavros Sachtouris | term.endswith(suffix))] |
120 | f724cd35 | Stavros Sachtouris | |
121 | a7aacf12 | Stavros Sachtouris | @property
|
122 | a7aacf12 | Stavros Sachtouris | def cli_specs(self): |
123 | 362adf50 | Stavros Sachtouris | suffix = '_cli'
|
124 | 362adf50 | Stavros Sachtouris | slen = len(suffix)
|
125 | 362adf50 | Stavros Sachtouris | return [(k[:-slen], v) for k, v in self.value.items('global') if ( |
126 | 362adf50 | Stavros Sachtouris | k.endswith(suffix))] |
127 | 362adf50 | Stavros Sachtouris | |
128 | 362adf50 | Stavros Sachtouris | def get_global(self, option): |
129 | 534e7bbb | Stavros Sachtouris | return self.value.get('global', option) |
130 | 362adf50 | Stavros Sachtouris | |
131 | 144b3551 | Stavros Sachtouris | def get_cloud(self, cloud, option): |
132 | 144b3551 | Stavros Sachtouris | return self.value.get_cloud(cloud, option) |
133 | 017d37ce | Stavros Sachtouris | |
134 | f17d6cb5 | Stavros Sachtouris | |
135 | a7aacf12 | Stavros Sachtouris | _config_arg = ConfigArgument('Path to config file')
|
136 | 017d37ce | Stavros Sachtouris | |
137 | fd5db045 | Stavros Sachtouris | |
138 | 1bd4f765 | Stavros Sachtouris | class RuntimeConfigArgument(Argument): |
139 | edb7fc1a | Stavros Sachtouris | """Set a run-time setting option (not persistent)"""
|
140 | edb7fc1a | Stavros Sachtouris | |
141 | c41a86b2 | Stavros Sachtouris | def __init__(self, config_arg, help='', parsed_name=None, default=None): |
142 | c41a86b2 | Stavros Sachtouris | super(self.__class__, self).__init__(1, help, parsed_name, default) |
143 | c41a86b2 | Stavros Sachtouris | self._config_arg = config_arg
|
144 | c41a86b2 | Stavros Sachtouris | |
145 | fd5db045 | Stavros Sachtouris | @property
|
146 | c41a86b2 | Stavros Sachtouris | def value(self): |
147 | 34c480f2 | Stavros Sachtouris | return super(RuntimeConfigArgument, self).value |
148 | fd5db045 | Stavros Sachtouris | |
149 | c41a86b2 | Stavros Sachtouris | @value.setter
|
150 | c41a86b2 | Stavros Sachtouris | def value(self, options): |
151 | c41a86b2 | Stavros Sachtouris | if options == self.default: |
152 | c41a86b2 | Stavros Sachtouris | return
|
153 | fd5db045 | Stavros Sachtouris | if not isinstance(options, list): |
154 | a517ff50 | Stavros Sachtouris | options = ['%s' % options]
|
155 | c41a86b2 | Stavros Sachtouris | for option in options: |
156 | c41a86b2 | Stavros Sachtouris | keypath, sep, val = option.partition('=')
|
157 | c41a86b2 | Stavros Sachtouris | if not sep: |
158 | de73876b | Stavros Sachtouris | raiseCLIError( |
159 | de73876b | Stavros Sachtouris | CLISyntaxError('Argument Syntax Error '),
|
160 | 24ff0a35 | Stavros Sachtouris | details=[ |
161 | 24ff0a35 | Stavros Sachtouris | '%s is missing a "="',
|
162 | 24ff0a35 | Stavros Sachtouris | ' (usage: -o section.key=val)' % option])
|
163 | c41a86b2 | Stavros Sachtouris | section, sep, key = keypath.partition('.')
|
164 | 0a0b9fb6 | Stavros Sachtouris | if not sep: |
165 | 0a0b9fb6 | Stavros Sachtouris | key = section |
166 | 0a0b9fb6 | Stavros Sachtouris | section = 'global'
|
167 | 0a0b9fb6 | Stavros Sachtouris | self._config_arg.value.override(
|
168 | 0a0b9fb6 | Stavros Sachtouris | section.strip(), |
169 | fd5db045 | Stavros Sachtouris | key.strip(), |
170 | fd5db045 | Stavros Sachtouris | val.strip()) |
171 | fd5db045 | Stavros Sachtouris | |
172 | c41a86b2 | Stavros Sachtouris | |
173 | c41a86b2 | Stavros Sachtouris | class FlagArgument(Argument): |
174 | edb7fc1a | Stavros Sachtouris | """
|
175 | 439926dd | Stavros Sachtouris | :value: true if set, false otherwise
|
176 | edb7fc1a | Stavros Sachtouris | """
|
177 | edb7fc1a | Stavros Sachtouris | |
178 | 40a9c357 | Stavros Sachtouris | def __init__(self, help='', parsed_name=None, default=False): |
179 | c41a86b2 | Stavros Sachtouris | super(FlagArgument, self).__init__(0, help, parsed_name, default) |
180 | c41a86b2 | Stavros Sachtouris | |
181 | fd5db045 | Stavros Sachtouris | |
182 | c41a86b2 | Stavros Sachtouris | class ValueArgument(Argument): |
183 | edb7fc1a | Stavros Sachtouris | """
|
184 | edb7fc1a | Stavros Sachtouris | :value type: string
|
185 | edb7fc1a | Stavros Sachtouris | :value returns: given value or default
|
186 | edb7fc1a | Stavros Sachtouris | """
|
187 | edb7fc1a | Stavros Sachtouris | |
188 | c41a86b2 | Stavros Sachtouris | def __init__(self, help='', parsed_name=None, default=None): |
189 | c41a86b2 | Stavros Sachtouris | super(ValueArgument, self).__init__(1, help, parsed_name, default) |
190 | c41a86b2 | Stavros Sachtouris | |
191 | fd5db045 | Stavros Sachtouris | |
192 | 0155548b | Stavros Sachtouris | class CommaSeparatedListArgument(ValueArgument): |
193 | 0155548b | Stavros Sachtouris | """
|
194 | 0155548b | Stavros Sachtouris | :value type: string
|
195 | 0155548b | Stavros Sachtouris | :value returns: list of the comma separated values
|
196 | 0155548b | Stavros Sachtouris | """
|
197 | 0155548b | Stavros Sachtouris | |
198 | 0155548b | Stavros Sachtouris | @property
|
199 | 0155548b | Stavros Sachtouris | def value(self): |
200 | 0155548b | Stavros Sachtouris | return self._value or list() |
201 | 0155548b | Stavros Sachtouris | |
202 | 0155548b | Stavros Sachtouris | @value.setter
|
203 | 0155548b | Stavros Sachtouris | def value(self, newvalue): |
204 | 0155548b | Stavros Sachtouris | self._value = newvalue.split(',') if newvalue else list() |
205 | 0155548b | Stavros Sachtouris | |
206 | 0155548b | Stavros Sachtouris | |
207 | e3d4d442 | Stavros Sachtouris | class IntArgument(ValueArgument): |
208 | edb7fc1a | Stavros Sachtouris | |
209 | fd5db045 | Stavros Sachtouris | @property
|
210 | e3d4d442 | Stavros Sachtouris | def value(self): |
211 | 439926dd | Stavros Sachtouris | """integer (type checking)"""
|
212 | e3d4d442 | Stavros Sachtouris | return getattr(self, '_value', self.default) |
213 | fd5db045 | Stavros Sachtouris | |
214 | e3d4d442 | Stavros Sachtouris | @value.setter
|
215 | e3d4d442 | Stavros Sachtouris | def value(self, newvalue): |
216 | 4a25486d | Stavros Sachtouris | if newvalue == self.default: |
217 | 4a25486d | Stavros Sachtouris | self._value = newvalue
|
218 | 4a25486d | Stavros Sachtouris | return
|
219 | e3d4d442 | Stavros Sachtouris | try:
|
220 | 4a25486d | Stavros Sachtouris | if int(newvalue) == float(newvalue): |
221 | 4a25486d | Stavros Sachtouris | self._value = int(newvalue) |
222 | 4a25486d | Stavros Sachtouris | else:
|
223 | 4a25486d | Stavros Sachtouris | raise ValueError('Raise int argument error') |
224 | e3d4d442 | Stavros Sachtouris | except ValueError: |
225 | 24ff0a35 | Stavros Sachtouris | raiseCLIError(CLISyntaxError( |
226 | 24ff0a35 | Stavros Sachtouris | 'IntArgument Error',
|
227 | c8e17a67 | Stavros Sachtouris | details=['Value %s not an int' % newvalue]))
|
228 | fd5db045 | Stavros Sachtouris | |
229 | e3d4d442 | Stavros Sachtouris | |
230 | 04d01cd4 | Stavros Sachtouris | class DateArgument(ValueArgument): |
231 | 04d01cd4 | Stavros Sachtouris | |
232 | b7ff6e0c | Stavros Sachtouris | DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
|
233 | 04d01cd4 | Stavros Sachtouris | |
234 | b7ff6e0c | Stavros Sachtouris | INPUT_FORMATS = [DATE_FORMAT, '%d-%m-%Y', '%H:%M:%S %d-%m-%Y'] |
235 | 04d01cd4 | Stavros Sachtouris | |
236 | 04d01cd4 | Stavros Sachtouris | @property
|
237 | ea4a21b8 | Stavros Sachtouris | def timestamp(self): |
238 | ea4a21b8 | Stavros Sachtouris | v = getattr(self, '_value', self.default) |
239 | ea4a21b8 | Stavros Sachtouris | return mktime(v.timetuple()) if v else None |
240 | ea4a21b8 | Stavros Sachtouris | |
241 | ea4a21b8 | Stavros Sachtouris | @property
|
242 | ea4a21b8 | Stavros Sachtouris | def formated(self): |
243 | ea4a21b8 | Stavros Sachtouris | v = getattr(self, '_value', self.default) |
244 | b7ff6e0c | Stavros Sachtouris | return v.strftime(self.DATE_FORMAT) if v else None |
245 | ea4a21b8 | Stavros Sachtouris | |
246 | ea4a21b8 | Stavros Sachtouris | @property
|
247 | 04d01cd4 | Stavros Sachtouris | def value(self): |
248 | ea4a21b8 | Stavros Sachtouris | return self.timestamp |
249 | 04d01cd4 | Stavros Sachtouris | |
250 | 04d01cd4 | Stavros Sachtouris | @value.setter
|
251 | 04d01cd4 | Stavros Sachtouris | def value(self, newvalue): |
252 | 2af87afc | Stavros Sachtouris | self._value = self.format_date(newvalue) if newvalue else self.default |
253 | 04d01cd4 | Stavros Sachtouris | |
254 | 04d01cd4 | Stavros Sachtouris | def format_date(self, datestr): |
255 | 04d01cd4 | Stavros Sachtouris | for format in self.INPUT_FORMATS: |
256 | 04d01cd4 | Stavros Sachtouris | try:
|
257 | 04d01cd4 | Stavros Sachtouris | t = dtm.strptime(datestr, format) |
258 | 04d01cd4 | Stavros Sachtouris | except ValueError: |
259 | 04d01cd4 | Stavros Sachtouris | continue
|
260 | b7ff6e0c | Stavros Sachtouris | return t # .strftime(self.DATE_FORMAT) |
261 | b7ff6e0c | Stavros Sachtouris | raiseCLIError(None, 'Date Argument Error', details=[ |
262 | b7ff6e0c | Stavros Sachtouris | '%s not a valid date' % datestr,
|
263 | b7ff6e0c | Stavros Sachtouris | 'Correct formats:\n\t%s' % self.INPUT_FORMATS]) |
264 | 04d01cd4 | Stavros Sachtouris | |
265 | 04d01cd4 | Stavros Sachtouris | |
266 | c41a86b2 | Stavros Sachtouris | class VersionArgument(FlagArgument): |
267 | edb7fc1a | Stavros Sachtouris | """A flag argument with that prints current version"""
|
268 | edb7fc1a | Stavros Sachtouris | |
269 | fd5db045 | Stavros Sachtouris | @property
|
270 | c41a86b2 | Stavros Sachtouris | def value(self): |
271 | 439926dd | Stavros Sachtouris | """bool"""
|
272 | c41a86b2 | Stavros Sachtouris | return super(self.__class__, self).value |
273 | fd5db045 | Stavros Sachtouris | |
274 | c41a86b2 | Stavros Sachtouris | @value.setter
|
275 | c41a86b2 | Stavros Sachtouris | def value(self, newvalue): |
276 | c41a86b2 | Stavros Sachtouris | self._value = newvalue
|
277 | f17d6cb5 | Stavros Sachtouris | if newvalue:
|
278 | c41a86b2 | Stavros Sachtouris | import kamaki |
279 | fd5db045 | Stavros Sachtouris | print('kamaki %s' % kamaki.__version__)
|
280 | fd5db045 | Stavros Sachtouris | |
281 | 9bdc89da | Stavros Sachtouris | |
282 | ca5528f1 | Stavros Sachtouris | class RepeatableArgument(Argument): |
283 | ca5528f1 | Stavros Sachtouris | """A value argument that can be repeated"""
|
284 | ca5528f1 | Stavros Sachtouris | |
285 | ca5528f1 | Stavros Sachtouris | def __init__(self, help='', parsed_name=None, default=[]): |
286 | ca5528f1 | Stavros Sachtouris | super(RepeatableArgument, self).__init__( |
287 | ca5528f1 | Stavros Sachtouris | -1, help, parsed_name, default)
|
288 | ca5528f1 | Stavros Sachtouris | |
289 | ca5528f1 | Stavros Sachtouris | |
290 | 0a0b9fb6 | Stavros Sachtouris | class KeyValueArgument(Argument): |
291 | ca5528f1 | Stavros Sachtouris | """A Key=Value Argument that can be repeated
|
292 | edb7fc1a | Stavros Sachtouris |
|
293 | 439926dd | Stavros Sachtouris | :syntax: --<arg> key1=value1 --<arg> key2=value2 ...
|
294 | edb7fc1a | Stavros Sachtouris | """
|
295 | edb7fc1a | Stavros Sachtouris | |
296 | 2d1202ee | Stavros Sachtouris | def __init__(self, help='', parsed_name=None, default=[]): |
297 | 0a0b9fb6 | Stavros Sachtouris | super(KeyValueArgument, self).__init__(-1, help, parsed_name, default) |
298 | f3e94e06 | Stavros Sachtouris | |
299 | fd5db045 | Stavros Sachtouris | @property
|
300 | f3e94e06 | Stavros Sachtouris | def value(self): |
301 | 439926dd | Stavros Sachtouris | """
|
302 | 8d427cb9 | Stavros Sachtouris | :returns: (dict) {key1: val1, key2: val2, ...}
|
303 | 439926dd | Stavros Sachtouris | """
|
304 | f3e94e06 | Stavros Sachtouris | return super(KeyValueArgument, self).value |
305 | fd5db045 | Stavros Sachtouris | |
306 | fd5db045 | Stavros Sachtouris | @value.setter
|
307 | f3e94e06 | Stavros Sachtouris | def value(self, keyvalue_pairs): |
308 | 8d427cb9 | Stavros Sachtouris | """
|
309 | 8d427cb9 | Stavros Sachtouris | :param keyvalue_pairs: (str) ['key1=val1', 'key2=val2', ...]
|
310 | 8d427cb9 | Stavros Sachtouris | """
|
311 | 2d1202ee | Stavros Sachtouris | self._value = getattr(self, '_value', self.value) or {} |
312 | f52b39db | Stavros Sachtouris | try:
|
313 | f52b39db | Stavros Sachtouris | for pair in keyvalue_pairs: |
314 | f52b39db | Stavros Sachtouris | key, sep, val = pair.partition('=')
|
315 | f52b39db | Stavros Sachtouris | assert sep, ' %s misses a "=" (usage: key1=val1 )\n' % (pair) |
316 | a75393fb | Stavros Sachtouris | self._value[key] = val
|
317 | f52b39db | Stavros Sachtouris | except Exception as e: |
318 | f52b39db | Stavros Sachtouris | raiseCLIError(e, 'KeyValueArgument Syntax Error')
|
319 | f3e94e06 | Stavros Sachtouris | |
320 | fd1f1d96 | Stavros Sachtouris | |
321 | fd1f1d96 | Stavros Sachtouris | class ProgressBarArgument(FlagArgument): |
322 | 439926dd | Stavros Sachtouris | """Manage a progress bar"""
|
323 | fd1f1d96 | Stavros Sachtouris | |
324 | fd1f1d96 | Stavros Sachtouris | def __init__(self, help='', parsed_name='', default=True): |
325 | fd1f1d96 | Stavros Sachtouris | self.suffix = '%(percent)d%%' |
326 | fd1f1d96 | Stavros Sachtouris | super(ProgressBarArgument, self).__init__(help, parsed_name, default) |
327 | fd1f1d96 | Stavros Sachtouris | |
328 | 852a22e7 | Stavros Sachtouris | def clone(self): |
329 | 439926dd | Stavros Sachtouris | """Get a modifiable copy of this bar"""
|
330 | 852a22e7 | Stavros Sachtouris | newarg = ProgressBarArgument( |
331 | c0fbf04c | Stavros Sachtouris | self.help, self.parsed_name, self.default) |
332 | 852a22e7 | Stavros Sachtouris | newarg._value = self._value
|
333 | 852a22e7 | Stavros Sachtouris | return newarg
|
334 | 852a22e7 | Stavros Sachtouris | |
335 | 8547cd19 | Stavros Sachtouris | def get_generator( |
336 | 8547cd19 | Stavros Sachtouris | self, message, message_len=25, countdown=False, timeout=100): |
337 | 439926dd | Stavros Sachtouris | """Get a generator to handle progress of the bar (gen.next())"""
|
338 | fd1f1d96 | Stavros Sachtouris | if self.value: |
339 | fd1f1d96 | Stavros Sachtouris | return None |
340 | fd1f1d96 | Stavros Sachtouris | try:
|
341 | 1d329d27 | Stavros Sachtouris | self.bar = KamakiProgressBar()
|
342 | fd1f1d96 | Stavros Sachtouris | except NameError: |
343 | 329753ae | Stavros Sachtouris | self.value = None |
344 | 329753ae | Stavros Sachtouris | return self.value |
345 | 8547cd19 | Stavros Sachtouris | if countdown:
|
346 | e9c73313 | Stavros Sachtouris | bar_phases = list(self.bar.phases) |
347 | 8547cd19 | Stavros Sachtouris | self.bar.empty_fill, bar_phases[0] = bar_phases[-1], '' |
348 | 8547cd19 | Stavros Sachtouris | bar_phases.reverse() |
349 | e9c73313 | Stavros Sachtouris | self.bar.phases = bar_phases
|
350 | 8547cd19 | Stavros Sachtouris | self.bar.bar_prefix = ' ' |
351 | e9c73313 | Stavros Sachtouris | self.bar.bar_suffix = ' ' |
352 | 8547cd19 | Stavros Sachtouris | self.bar.max = timeout or 100 |
353 | 8547cd19 | Stavros Sachtouris | self.bar.suffix = '%(remaining)ds to timeout' |
354 | e9c73313 | Stavros Sachtouris | else:
|
355 | e9c73313 | Stavros Sachtouris | self.bar.suffix = '%(percent)d%% - %(eta)ds' |
356 | 8547cd19 | Stavros Sachtouris | self.bar.eta = timeout or 100 |
357 | 329753ae | Stavros Sachtouris | self.bar.message = message.ljust(message_len)
|
358 | a10f5561 | Stavros Sachtouris | self.bar.start()
|
359 | fd1f1d96 | Stavros Sachtouris | |
360 | 852a22e7 | Stavros Sachtouris | def progress_gen(n): |
361 | 852a22e7 | Stavros Sachtouris | for i in self.bar.iter(range(int(n))): |
362 | 852a22e7 | Stavros Sachtouris | yield
|
363 | 852a22e7 | Stavros Sachtouris | yield
|
364 | 852a22e7 | Stavros Sachtouris | return progress_gen
|
365 | fd1f1d96 | Stavros Sachtouris | |
366 | 852a22e7 | Stavros Sachtouris | def finish(self): |
367 | 439926dd | Stavros Sachtouris | """Stop progress bar, return terminal cursor to user"""
|
368 | 852a22e7 | Stavros Sachtouris | if self.value: |
369 | 852a22e7 | Stavros Sachtouris | return
|
370 | 852a22e7 | Stavros Sachtouris | mybar = getattr(self, 'bar', None) |
371 | 852a22e7 | Stavros Sachtouris | if mybar:
|
372 | 852a22e7 | Stavros Sachtouris | mybar.finish() |
373 | fd1f1d96 | Stavros Sachtouris | |
374 | fd1f1d96 | Stavros Sachtouris | |
375 | de73876b | Stavros Sachtouris | _arguments = dict(
|
376 | de73876b | Stavros Sachtouris | config=_config_arg, |
377 | 144b3551 | Stavros Sachtouris | cloud=ValueArgument('Chose a cloud to connect to', ('--cloud')), |
378 | fd5db045 | Stavros Sachtouris | help=Argument(0, 'Show help message', ('-h', '--help')), |
379 | fd5db045 | Stavros Sachtouris | debug=FlagArgument('Include debug output', ('-d', '--debug')), |
380 | f6822a26 | Stavros Sachtouris | #include=FlagArgument(
|
381 | f6822a26 | Stavros Sachtouris | # 'Include raw connection data in the output', ('-i', '--include')),
|
382 | fd5db045 | Stavros Sachtouris | silent=FlagArgument('Do not output anything', ('-s', '--silent')), |
383 | fd5db045 | Stavros Sachtouris | verbose=FlagArgument('More info at response', ('-v', '--verbose')), |
384 | fd5db045 | Stavros Sachtouris | version=VersionArgument('Print current version', ('-V', '--version')), |
385 | 1bd4f765 | Stavros Sachtouris | options=RuntimeConfigArgument( |
386 | cb4a5d9c | Stavros Sachtouris | _config_arg, 'Override a config value', ('-o', '--options')) |
387 | 9bdc89da | Stavros Sachtouris | ) |
388 | cb4a5d9c | Stavros Sachtouris | |
389 | cb4a5d9c | Stavros Sachtouris | |
390 | cb4a5d9c | Stavros Sachtouris | # Initial command line interface arguments
|
391 | 439926dd | Stavros Sachtouris | |
392 | 439926dd | Stavros Sachtouris | |
393 | 7c2247a0 | Stavros Sachtouris | class ArgumentParseManager(object): |
394 | e0da0f90 | Stavros Sachtouris | """Manage (initialize and update) an ArgumentParser object"""
|
395 | e0da0f90 | Stavros Sachtouris | |
396 | e0da0f90 | Stavros Sachtouris | def __init__(self, exe, arguments=None): |
397 | e0da0f90 | Stavros Sachtouris | """
|
398 | e0da0f90 | Stavros Sachtouris | :param exe: (str) the basic command (e.g. 'kamaki')
|
399 | e0da0f90 | Stavros Sachtouris |
|
400 | e0da0f90 | Stavros Sachtouris | :param arguments: (dict) if given, overrides the global _argument as
|
401 | e0da0f90 | Stavros Sachtouris | the parsers arguments specification
|
402 | e0da0f90 | Stavros Sachtouris | """
|
403 | de73876b | Stavros Sachtouris | self.parser = ArgumentParser(
|
404 | 320aac17 | Stavros Sachtouris | add_help=False, formatter_class=RawDescriptionHelpFormatter)
|
405 | b3dd8f4b | Stavros Sachtouris | self.syntax = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe |
406 | e0da0f90 | Stavros Sachtouris | if arguments:
|
407 | e0da0f90 | Stavros Sachtouris | self.arguments = arguments
|
408 | e0da0f90 | Stavros Sachtouris | else:
|
409 | e0da0f90 | Stavros Sachtouris | global _arguments
|
410 | e0da0f90 | Stavros Sachtouris | self.arguments = _arguments
|
411 | 631b7c35 | Stavros Sachtouris | self._parser_modified, self._parsed, self._unparsed = False, None, None |
412 | 7c2247a0 | Stavros Sachtouris | self.parse()
|
413 | e0da0f90 | Stavros Sachtouris | |
414 | e0da0f90 | Stavros Sachtouris | @property
|
415 | e0da0f90 | Stavros Sachtouris | def syntax(self): |
416 | b3dd8f4b | Stavros Sachtouris | """The command syntax (useful for help messages, descriptions, etc)"""
|
417 | e0da0f90 | Stavros Sachtouris | return self.parser.prog |
418 | e0da0f90 | Stavros Sachtouris | |
419 | e0da0f90 | Stavros Sachtouris | @syntax.setter
|
420 | e0da0f90 | Stavros Sachtouris | def syntax(self, new_syntax): |
421 | e0da0f90 | Stavros Sachtouris | self.parser.prog = new_syntax
|
422 | e0da0f90 | Stavros Sachtouris | |
423 | b3dd8f4b | Stavros Sachtouris | @property
|
424 | b3dd8f4b | Stavros Sachtouris | def arguments(self): |
425 | 631b7c35 | Stavros Sachtouris | """:returns: (dict) arguments the parser should be aware of"""
|
426 | b3dd8f4b | Stavros Sachtouris | return self._arguments |
427 | b3dd8f4b | Stavros Sachtouris | |
428 | b3dd8f4b | Stavros Sachtouris | @arguments.setter
|
429 | b3dd8f4b | Stavros Sachtouris | def arguments(self, new_arguments): |
430 | 631b7c35 | Stavros Sachtouris | assert isinstance(new_arguments, dict), 'Arguments must be in a dict' |
431 | b3dd8f4b | Stavros Sachtouris | self._arguments = new_arguments
|
432 | b3dd8f4b | Stavros Sachtouris | self.update_parser()
|
433 | b3dd8f4b | Stavros Sachtouris | |
434 | 7c2247a0 | Stavros Sachtouris | @property
|
435 | b3dd8f4b | Stavros Sachtouris | def parsed(self): |
436 | 7c2247a0 | Stavros Sachtouris | """(Namespace) parser-matched terms"""
|
437 | b3dd8f4b | Stavros Sachtouris | if self._parser_modified: |
438 | b3dd8f4b | Stavros Sachtouris | self.parse()
|
439 | b3dd8f4b | Stavros Sachtouris | return self._parsed |
440 | b3dd8f4b | Stavros Sachtouris | |
441 | b3dd8f4b | Stavros Sachtouris | @property
|
442 | b3dd8f4b | Stavros Sachtouris | def unparsed(self): |
443 | b3dd8f4b | Stavros Sachtouris | """(list) parser-unmatched terms"""
|
444 | b3dd8f4b | Stavros Sachtouris | if self._parser_modified: |
445 | b3dd8f4b | Stavros Sachtouris | self.parse()
|
446 | b3dd8f4b | Stavros Sachtouris | return self._unparsed |
447 | b3dd8f4b | Stavros Sachtouris | |
448 | e0da0f90 | Stavros Sachtouris | def update_parser(self, arguments=None): |
449 | e0da0f90 | Stavros Sachtouris | """Load argument specifications to parser
|
450 | e0da0f90 | Stavros Sachtouris |
|
451 | e0da0f90 | Stavros Sachtouris | :param arguments: if not given, update self.arguments instead
|
452 | e0da0f90 | Stavros Sachtouris | """
|
453 | 631b7c35 | Stavros Sachtouris | arguments = arguments or self._arguments |
454 | e0da0f90 | Stavros Sachtouris | |
455 | e0da0f90 | Stavros Sachtouris | for name, arg in arguments.items(): |
456 | e0da0f90 | Stavros Sachtouris | try:
|
457 | e0da0f90 | Stavros Sachtouris | arg.update_parser(self.parser, name)
|
458 | b3dd8f4b | Stavros Sachtouris | self._parser_modified = True |
459 | e0da0f90 | Stavros Sachtouris | except ArgumentError:
|
460 | e0da0f90 | Stavros Sachtouris | pass
|
461 | e0da0f90 | Stavros Sachtouris | |
462 | 7c2247a0 | Stavros Sachtouris | def update_arguments(self, new_arguments): |
463 | 7c2247a0 | Stavros Sachtouris | """Add to / update existing arguments
|
464 | 7c2247a0 | Stavros Sachtouris |
|
465 | 7c2247a0 | Stavros Sachtouris | :param new_arguments: (dict)
|
466 | 7c2247a0 | Stavros Sachtouris | """
|
467 | 7c2247a0 | Stavros Sachtouris | if new_arguments:
|
468 | 7c2247a0 | Stavros Sachtouris | assert isinstance(new_arguments, dict) |
469 | 7c2247a0 | Stavros Sachtouris | self._arguments.update(new_arguments)
|
470 | 7c2247a0 | Stavros Sachtouris | self.update_parser()
|
471 | 7c2247a0 | Stavros Sachtouris | |
472 | 120126f1 | Stavros Sachtouris | def parse(self, new_args=None): |
473 | 320aac17 | Stavros Sachtouris | """Parse user input"""
|
474 | c8e17a67 | Stavros Sachtouris | try:
|
475 | 320aac17 | Stavros Sachtouris | pkargs = (new_args,) if new_args else () |
476 | 320aac17 | Stavros Sachtouris | self._parsed, unparsed = self.parser.parse_known_args(*pkargs) |
477 | c8e17a67 | Stavros Sachtouris | except SystemExit: |
478 | c8e17a67 | Stavros Sachtouris | raiseCLIError(CLISyntaxError('Argument Syntax Error'))
|
479 | b3dd8f4b | Stavros Sachtouris | for name, arg in self.arguments.items(): |
480 | b3dd8f4b | Stavros Sachtouris | arg.value = getattr(self._parsed, name, arg.default) |
481 | b3dd8f4b | Stavros Sachtouris | self._unparsed = []
|
482 | b3dd8f4b | Stavros Sachtouris | for term in unparsed: |
483 | b3dd8f4b | Stavros Sachtouris | self._unparsed += split_input(' \'%s\' ' % term) |
484 | b3dd8f4b | Stavros Sachtouris | self._parser_modified = False |