503a274722ddc78a06aac02c94469318a51e66b4
[kamaki] / kamaki / clients / commissioning / utils / argmap.py
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 try:
35     from collections import OrderedDict
36 except ImportError:
37     from kamaki.clients.commissioning.utils.ordereddict import OrderedDict
38 from itertools import chain
39 from cStringIO import StringIO
40
41 ARGMAP_MAGIC = '[=ARGMAP=]'
42
43
44 class arguments(object):
45     __slots__ = ('args', 'kw')
46
47     def __init__(self, *args, **kwargs):
48         self.args = list(args)
49         kw = OrderedDict()
50         kwitems = kw.pop('kwitems', None)
51         if kwitems is not None:
52             kw.update(kwitems)
53         kw.update(kwargs)
54         self.kw = kw
55
56     def __str__(self):
57         return str(self.args) + '+' + str(self.kw)
58
59     def __repr__(self):
60         return repr(self.args) + '+' + repr(self.kw)
61
62     def __getitem__(self, key):
63         if (isinstance(key, int)
64             or isinstance(key, long)
65             or isinstance(key, slice)):
66                 return self.args[key]
67         else:
68             return self.kw[key]
69
70     def __setitem__(self, key, value):
71         if (isinstance(key, int)
72             or isinstance(key, long)
73             or isinstance(key, slice)):
74                 self.args[key] = value
75         else:
76             self.kw[key] = value
77
78     def __delitem__(self, key):
79         if (isinstance(key, int)
80             or isinstance(key, long)
81             or isinstance(key, slice)):
82                 del self.args[key]
83         else:
84                 del self.kw[key]
85
86     def iteritems(self):
87         for item in self.args:
88             yield None, item
89         for k, v in self.kw:
90             yield k, v
91
92     def items(self):
93         return list(self.iteritems())
94
95     def iterkeys(self):
96         return self.kw.iterkeys()
97
98     def keys(self):
99         return self.kw.keys()
100
101     def itervalues(self):
102         return chain(self.args, self.kw.itervalues())
103
104     def values(self):
105         return list(self.itervalues)
106
107     def append(self, value):
108         self.args.append(value)
109
110
111 def argmap_encode(obj, output):
112     if obj is None:
113         output('[=null]')
114         return
115
116     if isinstance(obj, basestring):
117         if not obj:
118             output('""')
119         if isinstance(obj, unicode):
120             obj = obj.encode('utf-8')
121         output('"')
122         start = 0
123         while 1:
124             end = obj.find(start) + 1
125             if end < 0:
126                 break
127             output(obj[start:end] + '"')
128             start = end
129         output(obj[start:])
130         output('"')
131         return
132
133     if isinstance(obj, int) or isinstance(obj, long):
134         output(str(obj))
135         return
136
137     if hasattr(obj, 'iteritems'):
138         output('[')
139         once = 1
140         for k, v in obj.iteritems():
141             if once:
142                 once = 0
143             else:
144                 output(' ')
145             if k is not None:
146                 argmap_encode(k)
147                 output('=')
148             argmap_encode(v)
149         output(']')
150
151     if hasattr(obj, '__iter__'):
152         output('[')
153         once = 1
154         for item in obj:
155             if once:
156                 once = 0
157             else:
158                 output(' ')
159             argmap_encode(item)
160         output(']')
161
162     m = "Unsupported type '%s'" % (type(obj))
163     m += ''
164
165
166 def argmap_decode(inputf, s=None):
167     if isinstance(inputf, str):
168         inputf = StringIO(inputf).read
169
170     if s is None:
171         s = inputf(1)
172
173     while 1:
174         if not s.isspace():
175             break
176         s = inputf(1)
177
178     item = ''
179     if s == '"':
180         s = inputf(1)
181         while 1:
182             if s == '"':
183                 s = inputf(1)
184                 if s != '"':
185                     return item, s
186             item += s
187             s = inputf(1)
188     elif s == '[':
189         item, s = argmap_decode_args(inputf)
190         return item, s
191     else:
192         while 1:
193             item += s
194             s = inputf(1)
195             if s in ' =]':
196                 return item, s
197
198
199 def argmap_decode_atom(inputf):
200     s = inputf(4)
201     if s != 'null':
202         m = "Invalid atom '%s'" % (s,)
203         raise ValueError(m)
204     return None, None
205
206
207 def argmap_decode_args(inputf):
208     args = []
209     append = args.append
210     s = inputf(1)
211     key = None
212
213     while 1:
214         if s is None:
215             s = inputf(1)
216
217         if s == ']':
218             if key is not None:
219                 append((None, key))
220             args.append(ARGMAP_MAGIC)
221             return args, None
222
223         if s == '=':
224             if key is None:
225                 atom, s = argmap_decode_atom(inputf)
226                 append((None, atom))
227             else:
228                 value, s = argmap_decode(inputf)
229                 append((key, value))
230                 key = None
231         elif s == ' ':
232             if key is not None:
233                 append((None, key))
234                 key = None
235             s = inputf(1)
236         elif s == '':
237             m = "EOF while scanning for ']'"
238             raise ValueError(m)
239         else:
240             if key is not None:
241                 append((None, key))
242             key, s = argmap_decode(inputf, s=s)
243
244
245 def argmap_check(obj):
246     if hasattr(obj, 'keys') and callable(obj.keys):
247         # this could cover both cases
248         return ARGMAP_MAGIC in obj
249     if hasattr(obj, '__len__'):
250         length = len(obj)
251         return length and obj[length - 1] == ARGMAP_MAGIC
252     return False
253
254
255 def argmap_unzip_dict(argmap):
256     if not hasattr(argmap, 'keys'):
257         m = "argmap unzip dict: not a dict"
258         raise TypeError(m)
259     if ARGMAP_MAGIC not in argmap:
260         m = "argmap unzip dict: magic not found"
261         raise ValueError(m)
262     args = argmap.pop(None, [])
263     kw = OrderedDict(argmap)
264     del kw[ARGMAP_MAGIC]
265     return args, kw
266
267
268 def argmap_unzip_list(argmap):
269     if not argmap or argmap.pop() != ARGMAP_MAGIC:
270         m = "argmap unzip list: magic not found"
271         raise ValueError(m)
272
273     args = []
274     append = args.append
275     kw = OrderedDict()
276     for k, v in argmap:
277         if k is None:
278             append(v)
279         else:
280             kw[k] = v
281     return args, kw
282
283
284 def argmap_unzip(argmap):
285     if hasattr(argmap, 'keys'):
286         return argmap_unzip_dict(argmap)
287     elif hasattr(argmap, '__iter__'):
288         return argmap_unzip_list(argmap)
289     else:
290         m = "argmap: cannot unzip type %s" % (type(argmap),)
291         raise ValueError(m)
292
293
294 def argmap_zip_list(args, kw):
295     return [(None, a) for a in args] + kw.items() + [ARGMAP_MAGIC]
296
297
298 def argmap_zip_dict(args, kw):
299     argmap = OrderedDict()
300     argmap.update(kw)
301     argmap[ARGMAP_MAGIC] = ARGMAP_MAGIC
302     argmap[None] = list(args) + (argmap[None] if None in argmap else [])
303     return argmap
304
305 argmap_zip = argmap_zip_list
306
307
308 def argmap_list_to_dict(argmap):
309     args, kw = argmap_unzip_list(argmap)
310     kw[ARGMAP_MAGIC] = ARGMAP_MAGIC
311     kw[None] = args
312     return kw
313
314
315 def argmap_dict_to_list(argmap):
316     args, kw = argmap_unzip_dict(argmap)
317     return args + kw.items() + [ARGMAP_MAGIC]
318
319
320 def argmap_unpack_list(argmap):
321     kw = argmap_list_to_dict(argmap)
322     if len(kw) == 2:
323         return kw[None]
324     return kw
325
326
327 def argmap_unpack_dict(argmap):
328     if hasattr(argmap, 'keys') and callable(argmap.keys):
329         return argmap_dict_to_list(argmap)[:-1]
330     return argmap[:-1]