Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / argument.py @ fd5db045

History | View | Annotate | Download (7.7 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#     copyright notice, this list of conditions and the following
9
#     disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#     copyright notice, this list of conditions and the following
13
#     disclaimer in the documentation and/or other materials
14
#     provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from sys import exit
35

    
36
from kamaki.cli.config import Config
37
from kamaki.cli.errors import CLISyntaxError
38

    
39

    
40
class Argument(object):
41
    """An argument that can be parsed from command line or otherwise"""
42

    
43
    def __init__(self, arity, help=None, parsed_name=None, default=None):
44
        self.arity = int(arity)
45

    
46
        if help is not None:
47
            self.help = help
48
        if parsed_name is not None:
49
            self.parsed_name = parsed_name
50
        if default is not None:
51
            self.default = default
52

    
53
    @property
54
    def parsed_name(self):
55
        return getattr(self, '_parsed_name', None)
56

    
57
    @parsed_name.setter
58
    def parsed_name(self, newname):
59
        self._parsed_name = getattr(self, '_parsed_name', [])
60
        if isinstance(newname, list) or isinstance(newname, tuple):
61
            self._parsed_name += list(newname)
62
        else:
63
            self._parsed_name.append(unicode(newname))
64

    
65
    @property
66
    def help(self):
67
        return getattr(self, '_help', None)
68

    
69
    @help.setter
70
    def help(self, newhelp):
71
        self._help = unicode(newhelp)
72

    
73
    @property
74
    def arity(self):
75
        return getattr(self, '_arity', None)
76

    
77
    @arity.setter
78
    def arity(self, newarity):
79
        newarity = int(newarity)
80
        assert newarity >= 0
81
        self._arity = newarity
82

    
83
    @property
84
    def default(self):
85
        if not hasattr(self, '_default'):
86
            self._default = False if self.arity == 0 else None
87
        return self._default
88

    
89
    @default.setter
90
    def default(self, newdefault):
91
        self._default = newdefault
92

    
93
    @property
94
    def value(self):
95
        return getattr(self, '_value', self.default)
96

    
97
    @value.setter
98
    def value(self, newvalue):
99
        self._value = newvalue
100

    
101
    def update_parser(self, parser, name):
102
        """Update an argument parser with this argument info"""
103
        action = 'store_true' if self.arity == 0 else 'store'
104
        parser.add_argument(*self.parsed_name, dest=name, action=action,
105
            default=self.default, help=self.help)
106

    
107
    def main(self):
108
        """Overide this method to give functionality to ur args"""
109
        raise NotImplementedError
110

    
111

    
112
class ConfigArgument(Argument):
113
    @property
114
    def value(self):
115
        return super(self.__class__, self).value
116

    
117
    @value.setter
118
    def value(self, config_file):
119
        self._value = Config(config_file) if config_file else Config()
120

    
121
    def get(self, group, term):
122
        return self.value.get(group, term)
123

    
124
    def get_groups(self):
125
        return self.value.apis()
126

    
127
_config_arg = ConfigArgument(1, 'Path to configuration file', '--config')
128

    
129

    
130
class CmdLineConfigArgument(Argument):
131
    def __init__(self, config_arg, help='', parsed_name=None, default=None):
132
        super(self.__class__, self).__init__(1, help, parsed_name, default)
133
        self._config_arg = config_arg
134

    
135
    @property
136
    def value(self):
137
        return super(self.__class__, self).value
138

    
139
    @value.setter
140
    def value(self, options):
141
        if options == self.default:
142
            return
143
        if not isinstance(options, list):
144
            options = [unicode(options)]
145
        for option in options:
146
            keypath, sep, val = option.partition('=')
147
            if not sep:
148
                raise CLISyntaxError(
149
                    details='Missing = (usage: -o section.key=val)')
150
            section, sep, key = keypath.partition('.')
151
            if not sep:
152
                raise CLISyntaxError(
153
                    details='Missing . (usage: -o section.key=val)')
154
        self._config_arg.value.override(section.strip(),
155
            key.strip(),
156
            val.strip())
157

    
158

    
159
class FlagArgument(Argument):
160
    def __init__(self, help='', parsed_name=None, default=None):
161
        super(FlagArgument, self).__init__(0, help, parsed_name, default)
162

    
163

    
164
class ValueArgument(Argument):
165
    def __init__(self, help='', parsed_name=None, default=None):
166
        super(ValueArgument, self).__init__(1, help, parsed_name, default)
167

    
168

    
169
class IntArgument(ValueArgument):
170
    @property
171
    def value(self):
172
        return getattr(self, '_value', self.default)
173

    
174
    @value.setter
175
    def value(self, newvalue):
176
        if newvalue == self.default:
177
            self._value = self.default
178
            return
179
        try:
180
            self._value = int(newvalue)
181
        except ValueError:
182
            raise CLISyntaxError('IntArgument Error',
183
                details='Value %s not an int' % newvalue)
184

    
185

    
186
class VersionArgument(FlagArgument):
187
    @property
188
    def value(self):
189
        return super(self.__class__, self).value
190

    
191
    @value.setter
192
    def value(self, newvalue):
193
        self._value = newvalue
194
        self.main()
195

    
196
    def main(self):
197
        if self.value:
198
            import kamaki
199
            print('kamaki %s' % kamaki.__version__)
200

    
201

    
202
class KeyValueArgument(ValueArgument):
203
    def __init__(self, help='', parsed_name=None, default={}):
204
        super(KeyValueArgument, self).__init__(help, parsed_name, default)
205

    
206
    @property
207
    def value(self):
208
        return super(KeyValueArgument, self).value
209

    
210
    @value.setter
211
    def value(self, keyvalue_pairs):
212
        if keyvalue_pairs == self.default:
213
            return
214
        if isinstance(keyvalue_pairs, str):
215
            keyvalue_pairs = keyvalue_pairs.strip().split(',')
216
        self._value = self.default
217
        for pair in keyvalue_pairs:
218
            key, sep, val = pair.partition('=')
219
            if not sep:
220
                raise CLISyntaxError(
221
                    details='Missing "="" (usage: key1=val1,key2=val2 ... )')
222
            self._value[key.strip()] = val.strip()
223

    
224
_arguments = dict(config=_config_arg,
225
    help=Argument(0, 'Show help message', ('-h', '--help')),
226
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
227
    include=FlagArgument('Include protocol headers in the output',
228
        ('-i', '--include')),
229
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
230
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
231
    version=VersionArgument('Print current version', ('-V', '--version')),
232
    options=CmdLineConfigArgument(_config_arg,
233
        'Override a config value',
234
        ('-o', '--options'))
235
)
236

    
237

    
238
def parse_known_args(parser, arguments=None):
239
    parsed, unparsed = parser.parse_known_args()
240
    for name, arg in arguments.items():
241
        arg.value = getattr(parsed, name, arg.default)
242
    return parsed, unparsed