root / commissioning / hlapi / util.py @ de34ce75
History | View | Annotate | Download (12.9 kB)
1 |
import sys |
---|---|
2 |
|
3 |
def isstr(s): |
4 |
return issubclass(type(s), basestring) |
5 |
|
6 |
def compatible_type(given_t, expected_t): |
7 |
if issubclass(expected_t, basestring): |
8 |
expected_t = basestring
|
9 |
if issubclass(given_t, basestring): |
10 |
given_t = basestring
|
11 |
return given_t == expected_t
|
12 |
|
13 |
|
14 |
def compatible_input_types(given, expected): |
15 |
# print "given=%s, expected=%s" % (given, expected)
|
16 |
if len(given) != len(expected): |
17 |
return False |
18 |
for i in xrange(len(given)): |
19 |
if not compatible_type(given[i], expected[i]): |
20 |
return False |
21 |
return True |
22 |
|
23 |
|
24 |
def compatible_return_type(given, expected): |
25 |
return compatible_type(given, expected)
|
26 |
|
27 |
|
28 |
def method_accepts(*types): |
29 |
'''Method decorator. Checks decorated function's arguments are
|
30 |
of the expected types. The self argument is ignored. This is
|
31 |
based on the ``accepts`` decorator.
|
32 |
|
33 |
Parameters:
|
34 |
types -- The expected types of the inputs to the decorated function.
|
35 |
Must specify type for each parameter.
|
36 |
'''
|
37 |
try:
|
38 |
def decorator(f): |
39 |
def newf(*args): |
40 |
args_to_check = args[1:] # Throw away self (or cls) |
41 |
assert len(args_to_check) == len(types) |
42 |
if len(args_to_check) != len(types): |
43 |
raise TypeError("Wrong number of arguments. Expected is %s, given is %s" % (len(types), len(args_to_check))) |
44 |
argtypes = tuple(map(type, args_to_check)) |
45 |
# if argtypes != types:
|
46 |
if not compatible_input_types(argtypes, types): |
47 |
msg = info(f.__name__, types, argtypes, 0)
|
48 |
raise TypeError, msg |
49 |
return f(*args)
|
50 |
newf.__name__ = f.__name__ |
51 |
return newf
|
52 |
return decorator
|
53 |
except KeyError, key: |
54 |
raise KeyError, key + "is not a valid keyword argument" |
55 |
except TypeError, msg: |
56 |
raise TypeError, msg |
57 |
|
58 |
# http://wiki.python.org/moin/PythonDecoratorLibrary#Type_Enforcement_.28accepts.2Freturns.29
|
59 |
# Slightly modified to always raise an error
|
60 |
def accepts(*types): |
61 |
'''Function decorator. Checks decorated function's arguments are
|
62 |
of the expected types.
|
63 |
|
64 |
Parameters:
|
65 |
types -- The expected types of the inputs to the decorated function.
|
66 |
Must specify type for each parameter.
|
67 |
'''
|
68 |
try:
|
69 |
def decorator(f): |
70 |
def newf(*args): |
71 |
if len(args) != len(types): |
72 |
raise TypeError("Wrong number of arguments. Expected is %s, given is %s" % (len(types), len(args))) |
73 |
argtypes = tuple(map(type, args)) |
74 |
# if argtypes != types:
|
75 |
if not compatible_input_types(argtypes, types): |
76 |
msg = info(f.__name__, types, argtypes, 0)
|
77 |
raise TypeError, msg |
78 |
return f(*args)
|
79 |
newf.__name__ = f.__name__ |
80 |
return newf
|
81 |
return decorator
|
82 |
except KeyError, key: |
83 |
raise KeyError, key + "is not a valid keyword argument" |
84 |
except TypeError, msg: |
85 |
raise TypeError, msg |
86 |
|
87 |
# http://wiki.python.org/moin/PythonDecoratorLibrary#Type_Enforcement_.28accepts.2Freturns.29
|
88 |
# Slightly modified to always raise an error
|
89 |
def returns(ret_type): |
90 |
'''Function decorator. Checks decorated function's return value
|
91 |
is of the expected type.
|
92 |
|
93 |
Parameters:
|
94 |
ret_type -- The expected type of the decorated function's return value.
|
95 |
Must specify type for each parameter.
|
96 |
'''
|
97 |
try:
|
98 |
def decorator(f): |
99 |
def newf(*args): |
100 |
result = f(*args) |
101 |
res_type = type(result)
|
102 |
print "ret_type=%s, res_type=%s" % (ret_type, res_type) |
103 |
# if res_type != ret_type:
|
104 |
if not compatible_type(res_type, ret_type): |
105 |
msg = info(f.__name__, (ret_type,), (res_type,), 1)
|
106 |
raise TypeError, msg |
107 |
return result
|
108 |
newf.__name__ = f.__name__ |
109 |
return newf
|
110 |
return decorator
|
111 |
except KeyError, key: |
112 |
raise KeyError, key + "is not a valid keyword argument" |
113 |
except TypeError, msg: |
114 |
raise TypeError, msg |
115 |
|
116 |
|
117 |
# http://wiki.python.org/moin/PythonDecoratorLibrary#Type_Enforcement_.28accepts.2Freturns.29
|
118 |
def info(fname, expected, actual, flag): |
119 |
'''Convenience function returns nicely formatted error/warning msg.'''
|
120 |
format = lambda types: ', '.join([str(t).split("'")[1] for t in types]) |
121 |
expected, actual = format(expected), format(actual) |
122 |
msg = "'{0}' method ".format( fname )\
|
123 |
+ ("accepts", "returns")[flag] + " ({0}), but ".format(expected)\ |
124 |
+ ("was given", "result is")[flag] + " ({0})".format(actual) |
125 |
return msg
|
126 |
|
127 |
|
128 |
def check_string(label, value): |
129 |
if not issubclass(type(value), basestring): |
130 |
raise Exception( |
131 |
"%s is not a string, but a(n) %s with value %s" % (
|
132 |
label, type(value), value))
|
133 |
return value
|
134 |
|
135 |
|
136 |
def check_context(context): |
137 |
assert isinstance(context, dict) |
138 |
return context
|
139 |
|
140 |
|
141 |
@accepts(str, str) |
142 |
@returns(bool) |
143 |
def is_abs_name(name, label='name'): |
144 |
check_string(label, name) |
145 |
return (name == 'system') or name.startswith('system/') |
146 |
|
147 |
|
148 |
def check_abs_name(abs_name, label = 'abs_name'): |
149 |
check_string(label, abs_name) |
150 |
if len(abs_name) == 0: |
151 |
raise Exception("Absolute %s is empty" % (label,)) |
152 |
|
153 |
if abs_name.startswith('/'): |
154 |
raise Exception( |
155 |
"Absolute %s='%s' starts with '/'" % (label, abs_name))
|
156 |
|
157 |
if abs_name.endswith('/'): |
158 |
raise Exception( |
159 |
"Absolute %s='%s' ends with '/'" % (label, abs_name))
|
160 |
|
161 |
if (abs_name != 'system') and (not abs_name.startswith('system/')): |
162 |
raise Exception( |
163 |
"Unknown hierarchy for %s='%s'. Should be 'system' or start with 'system/'" % (
|
164 |
label, abs_name)) |
165 |
|
166 |
if abs_name.find('//') >= 0: |
167 |
raise Exception("// is not allowed in %s='%s'" % ( |
168 |
label, abs_name)) |
169 |
|
170 |
return abs_name
|
171 |
|
172 |
def check_relative_name(relative_name, label='relative_name'): |
173 |
check_string(label, relative_name) |
174 |
if relative_name.startswith('system') or relative_name.startswith('system/'): |
175 |
raise Exception("%s (='%s'), which was intended as relative, has absolute form" % (label, relative_name)) |
176 |
|
177 |
return relative_name
|
178 |
|
179 |
def check_name(name, label='name'): |
180 |
check_string(label, name) |
181 |
if len(name) == 0: |
182 |
raise Exception("Name %s is empty" % (label,)) |
183 |
|
184 |
if name.find('//') >= 0: |
185 |
raise Exception("// is not allowed in %s (='%s')" % ( |
186 |
label, name)) |
187 |
|
188 |
return name
|
189 |
|
190 |
|
191 |
def check_abs_global_resource_name(abs_resource_name, |
192 |
label='abs_resource_name'):
|
193 |
"""
|
194 |
``abs_resource_name`` must always be a string in absolute form.
|
195 |
"""
|
196 |
check_abs_name(abs_resource_name, label) |
197 |
if not abs_resource_name.startswith(NameOfResourcesNode): |
198 |
raise Exception("'%s' is not a global resource name" % (abs_resource_name,)) |
199 |
|
200 |
return abs_resource_name
|
201 |
|
202 |
|
203 |
def check_node_key(node_key): |
204 |
if node_key is not None: |
205 |
check_string('node_key', node_key)
|
206 |
return node_key
|
207 |
|
208 |
|
209 |
def is_system_node(node_name): |
210 |
check_string('node_name', node_name)
|
211 |
return node_name == 'system' |
212 |
|
213 |
|
214 |
def level_of_node(node_name): |
215 |
"""
|
216 |
Returns the level of a node in the absolute hierarchy, that is under
|
217 |
node 'system'.
|
218 |
|
219 |
By convention, 'system' has level zero.
|
220 |
"""
|
221 |
check_abs_name(node_name, 'node_name')
|
222 |
len(node_name.split('/')) - 1 |
223 |
|
224 |
|
225 |
@accepts(str, str) |
226 |
@returns(bool) |
227 |
def is_child_of_abs_name(child, parent): |
228 |
check_abs_name(parent) |
229 |
return child.startswith(parent) and child != parent |
230 |
|
231 |
|
232 |
def parent_abs_name_of(abs_name, label='abs_name'): |
233 |
"""
|
234 |
Given an absolute name, it returns its parent.
|
235 |
If ``abs_name`` is 'system' then it returns 'system', since this is
|
236 |
the convention of Quota Holder.
|
237 |
"""
|
238 |
if is_system_node(abs_name):
|
239 |
return abs_name
|
240 |
else:
|
241 |
check_abs_name(abs_name, label) |
242 |
|
243 |
# For 'a/b/c' we get ['a', 'b', 'c']
|
244 |
# And for 'a' we get ['a']
|
245 |
elements = abs_name.split('/')
|
246 |
|
247 |
if len(elements) == 1: |
248 |
# This must be normally caught by the call check_abs_name(abs_name)
|
249 |
# but let's be safe anyway
|
250 |
raise Exception( |
251 |
"Only 'system' is the top level entity, you provided '%s'" % (
|
252 |
abs_name)) |
253 |
else:
|
254 |
upto_name = '/'.join(elements[:-1]) |
255 |
return upto_name
|
256 |
|
257 |
|
258 |
@accepts(str) |
259 |
@returns(str) |
260 |
def last_part_of_abs_name(abs_name, label='abs_name'): |
261 |
"""
|
262 |
Given an absolute abs_name, which is made of simple parts separated with
|
263 |
slashes), computes the last part, that is the one after the last
|
264 |
slash.
|
265 |
"""
|
266 |
check_abs_name(abs_name, label) |
267 |
last_part = abs_name.split('/')[-1:] |
268 |
return last_part
|
269 |
|
270 |
|
271 |
@accepts(str, str, str, str) |
272 |
@returns(str) |
273 |
def reparent_child_name_under(child_name, |
274 |
parent_node_name, |
275 |
child_label='child_name',
|
276 |
parent_label='parent_node_name'):
|
277 |
"""
|
278 |
Given a child name and an absolute parent node name, creates an
|
279 |
absolute name for the child so as to reside under the given parent.
|
280 |
"""
|
281 |
check_abs_name(parent_node_name, parent_label) |
282 |
|
283 |
if child_name == parent_node_name:
|
284 |
raise Exception( |
285 |
"%s is the same as %s (='%s')" % (
|
286 |
child_label, |
287 |
parent_label, |
288 |
parent_node_name)) |
289 |
|
290 |
# If already under this parent, we are set.
|
291 |
if child_name.startswith(parent_node_name):
|
292 |
return child_name
|
293 |
|
294 |
# Else, just make the new absolute name by concatenation
|
295 |
return '%s/%s' % (parent_node_name, child_name) |
296 |
|
297 |
|
298 |
def make_abs_group_name(group_name): |
299 |
check_name(group_name, 'group_name')
|
300 |
return reparent_child_name_under(child_name=group_name,
|
301 |
parent_node_name=NameOfGroupsNode, |
302 |
child_label='group_name',
|
303 |
parent_label='NameOfGroupsNode')
|
304 |
|
305 |
def make_abs_global_resource_name(global_resource_name): |
306 |
check_name(global_resource_name, 'global_resource_name')
|
307 |
return reparent_child_name_under(child_name=global_resource_name,
|
308 |
parent_node_name=NameOfResourcesNode, |
309 |
child_label='global_resource_name',
|
310 |
parent_label='NameOfResourcesNode')
|
311 |
|
312 |
def make_abs_user_name(user_name): |
313 |
check_name(user_name, 'user_name')
|
314 |
return reparent_child_name_under(child_name=user_name,
|
315 |
parent_node_name=NameOfUsersNode, |
316 |
child_label='user_name',
|
317 |
parent_label='NameOfUsersNode')
|
318 |
|
319 |
|
320 |
@accepts(str, str, str, str) |
321 |
@returns(str) |
322 |
def relative_child_name_under(child_name, |
323 |
parent_name, |
324 |
child_label='child_name',
|
325 |
parent_label='parent_name'):
|
326 |
check_abs_name(parent_name, parent_label) |
327 |
|
328 |
if child_name == parent_name:
|
329 |
raise Exception( |
330 |
"%s is the same as %s (='%s')" % (
|
331 |
child_label, |
332 |
parent_label, |
333 |
parent_name)) |
334 |
|
335 |
if not child_name.startswith(parent_name): |
336 |
raise Exception( |
337 |
"%s (='%s') is not a child of %s (='%s')" % (
|
338 |
child_label, |
339 |
child_name, |
340 |
parent_label, |
341 |
parent_name)) |
342 |
|
343 |
return child_name[len(parent_name) + 1:] |
344 |
|
345 |
|
346 |
@accepts(str, str) |
347 |
@returns(str) |
348 |
def make_rel_group_name(group_name, label='group_name'): |
349 |
check_name(group_name, label) |
350 |
return relative_child_name_under(child_name=group_name,
|
351 |
parent_name=NameOfGroupsNode, |
352 |
child_label='group_name',
|
353 |
parent_label='NameOfGroupsNode')
|
354 |
|
355 |
|
356 |
@accepts(str, str) |
357 |
@returns(str) |
358 |
def make_rel_global_resource_name(resource_name, label='resource_name'): |
359 |
check_name(resource_name, label) |
360 |
return relative_child_name_under(child_name=resource_name,
|
361 |
parent_name=NameOfResourcesNode, |
362 |
child_label='resource_name',
|
363 |
parent_label='NameOfResourcesNode')
|
364 |
|
365 |
|
366 |
@accepts(str, str) |
367 |
@returns(str) |
368 |
def make_rel_user_name(user_name, label='user_name'): |
369 |
check_name(user_name, label) |
370 |
return relative_child_name_under(child_name=user_name,
|
371 |
parent_name=NameOfUsersNode, |
372 |
child_label='user_name',
|
373 |
parent_label='NameOfUsersNode')
|
374 |
|
375 |
|
376 |
NameOfSystemNode = 'system'
|
377 |
NameOfResourcesNode = 'system/resources'
|
378 |
NameOfGroupsNode = 'system/groups'
|
379 |
NameOfUsersNode = 'system/users'
|
380 |
|
381 |
|
382 |
ResourceAttributePrefixes = { |
383 |
NameOfResourcesNode: 'r',
|
384 |
NameOfGroupsNode: 'g',
|
385 |
NameOfUsersNode: 'u'
|
386 |
} |
387 |
|