7 This module is an implementation of eHive's Param module. 8 It defines ParamContainer which is an attribute of BaseRunnable 9 and not its base class as in eHive's class hierarchy. 10 All the specific warnings and exceptions inherit from ParamWarning 16 """Used by Process.BaseRunnable""" 20 class ParamException(Exception):
21 """Base class for parameters-related exceptions""" 24 """Raised when the parameter name is not a string""" 26 return '"{0}" (type {1}) is not a valid parameter name'.format(self.args[0], type(self.args[0]).__name__)
28 """Raised when ParamContainer tried to substitute an unexpected structure (only dictionaries and lists are accepted)""" 30 return 'Cannot substitute elements in objects of type "{0}"'.format(str(type(self.args[0])))
32 """Raised when parameters depend on each other, forming a loop""" 34 return "Substitution loop has been detected on {0}. Parameter-substitution stack: {1}".format(self.args[0], list(self.args[1].keys()))
38 """Equivalent of eHive's Param module""" 40 def __init__(self, unsubstituted_params, debug=False):
41 """Constructor. "unsubstituted_params" is a dictionary""" 42 self.unsubstituted_param_hash = unsubstituted_params.copy()
51 """Setter. Returns the new value""" 58 """Getter. Performs the parameter substitution""" 64 except (KeyError, SyntaxError, ParamException)
as e:
66 raise type(e)(*e.args)
from None 69 """Returns a boolean. It checks both substituted and unsubstituted parameters""" 78 """Tells whether "param_name" is a non-empty string""" 79 return isinstance(param_name, str)
and (param_name !=
'')
82 """Print debug information if the debug flag is turned on (cf constructor)""" 84 print(*args, **kwargs)
87 """Equivalent of get_param() that assumes "param_name" is a valid parameter name and hence, doesn't have to raise ParamNameException. 88 It is only used internally""" 98 Take any structure and replace the pairs of hashes with the values of the parameters / expression they represent 99 Compatible types: numbers, strings, lists, dictionaries (otherwise, ParamSubstitutionException is raised) 103 if structure
is None:
106 elif isinstance(structure, list):
109 elif isinstance(structure, dict):
114 elif isinstance(structure, numbers.Number):
117 elif isinstance(structure, str):
121 if structure[:6] ==
'#expr(' and structure[-6:] ==
')expr#' and structure.count(
'#expr(', 6, -6) == 0
and structure.count(
')expr#', 6, -6) == 0:
124 if structure[0] ==
'#' and structure[-1] ==
'#' and structure.count(
'#', 1, -1) == 0:
125 if len(structure) <= 2:
138 Parse "structure" and replace all the pairs of hashes by the result of calling callback() on the pair content 139 #expr()expr# are treated differently by calling subst_one_hashpair() 140 The result is a string (like structure) 145 (head,_,tmp) = structure.partition(
'#')
148 return ''.join(result)
149 if tmp.startswith(
'expr('):
150 i = tmp.find(
')expr#')
152 raise SyntaxError(
"Unmatched '#expr(' token")
156 (middle_param,_,tail) = tmp.partition(
'#')
158 raise SyntaxError(
"Unmatched '#' token")
159 if middle_param ==
'':
162 val = callback(middle_param)
163 result.append(str(val))
169 Run the parameter substitution for a single pair of hashes. 170 Here, we only need to handle #expr()expr#, #func:params# and #param_name# 171 as each condition has been parsed in the other methods 173 self.
debug_print(
"subst_one_hashpair", inside_hashes, is_expr)
182 s = self.
subst_all_hashpairs(inside_hashes[5:-5].strip(),
lambda middle_param:
'self.internal_get_param("{0}")'.format(middle_param))
185 elif ':' in inside_hashes:
186 (func_name,_,parameters) = inside_hashes.partition(
':')
190 raise SyntaxError(
"Unknown method: " + func_name)
197 raise SyntaxError(func_name +
" is not callable")
211 (
'delta' ,
'#expr( #alpha#*#beta# )expr#'),
213 (
'gamma' , [10,20,33,15]),
214 (
'gamma_prime' ,
'#expr( #gamma# )expr#'),
215 (
'gamma_second' ,
'#expr( list(#gamma#) )expr#'),
217 (
'age' , {
'Alice' : 17,
'Bob' : 20,
'Chloe' : 21}),
218 (
'age_prime' ,
'#expr( #age# )expr#'),
219 (
'age_second' ,
'#expr( dict(#age#) )expr#'),
221 (
'csv' ,
'[123,456,789]'),
222 (
'csv_prime' ,
'#expr( #csv# )expr#'),
223 (
'listref' ,
'#expr( eval(#csv#) )expr#'),
226 (
'ref_null' ,
'#null#'),
227 (
'ref2_null' ,
'#expr( #null# )expr#'),
228 (
'ref3_null' ,
'#alpha##null##beta#'),
233 def print_title(title):
235 print(
"*" + title +
"*")
237 def print_substitution(title, param_string):
239 print(
"\t>", param_string)
240 x = p.param_substitute(param_string)
243 def print_param_value(x):
244 print(
"\t=", x, type(x),
"id=0x{0:012x}".format(id(x)))
246 print_title(
"Exceptions")
248 p.get_param(
'ppppppp')
249 except KeyError
as e:
250 print(
"KeyError raised")
252 print(
"KeyError NOT raised")
257 except ParamNameException
as e:
258 print(
"ParamNameException raised")
260 print(
"ParamNameException NOT raised")
265 except ParamInfiniteLoopException
as e:
266 print(
"ParamInfiniteLoopException raised")
268 print(
"ParamInfiniteLoopException NOT raised")
271 print_title(
'All the parameters')
272 for (key,value)
in seed_params:
273 print(
"\t>", key,
"is seeded as:", value, type(value))
278 print_title(
"Numbers")
279 print_substitution(
"Scalar substitutions",
"#alpha# and another: #beta# and again one: #alpha# and the other: #beta# . Their product: #delta#" )
282 print_substitution(
"default stringification of gamma",
"#gamma#" )
283 print_substitution(
"expr-stringification of gamma",
"#expr( #gamma# )expr#" )
284 print_substitution(
"complex join of gamma",
"#expr( '~'.join([str(_) for _ in sorted(#gamma#)]) )expr#" )
285 print_substitution(
"complex join of gamma_prime",
"#expr( '~'.join([str(_) for _ in sorted(#gamma_prime#)]) )expr#" )
287 print_title(
"Global methods")
288 print_substitution(
"sum(gamma)",
"#expr( sum(#gamma#) )expr#" )
289 print_substitution(
"min(gamma)",
"#expr( min(#gamma#) )expr#" )
290 print_substitution(
"max(gamma)",
"#expr( max(#gamma#) )expr#" )
292 print_title(
"Dictionaries")
293 print_substitution(
"default stringification of age",
"#age#" )
294 print_substitution(
"expr-stringification of age",
"#expr( #age# )expr#" )
295 print_substitution(
"complex fold of age",
'#expr( "\t".join(["{0} is {1} years old".format(p,a) for (p,a) in #age#.items()]) )expr#' )
296 print_substitution(
"complex fold of age_prime",
'#expr( "\t".join(["{0} is {1} years old".format(p,a) for (p,a) in #age_prime#.items()]) )expr#' )
298 print_title(
"With indexes")
299 print_substitution(
"adding indexed values",
'#expr( #age#["Alice"]+max(#gamma#)+#listref#[0] )expr#' )
301 print_title(
"Modifications of gamma")
302 p.get_param(
'gamma').append(
"val0")
303 print(
"\tgamma", p.get_param(
'gamma'))
304 print(
"\tgamma_prime", p.get_param(
'gamma_prime'))
305 print(
"\tgamma_second", p.get_param(
'gamma_second'))
308 if __name__ ==
'__main__':
Base class for parameters-related exceptions.
Raised when ParamContainer tried to substitute an unexpected structure (only dictionaries and lists a...
def validate_parameter_name(self, param_name)
Tells whether "param_name" is a non-empty string.
def subst_all_hashpairs(self, structure, callback)
Parse "structure" and replace all the pairs of hashes by the result of calling callback() on the pair...
def get_param(self, param_name)
Getter.
def debug_print(self, args, kwargs)
Print debug information if the debug flag is turned on (cf constructor)
def internal_get_param(self, param_name)
Equivalent of get_param() that assumes "param_name" is a valid parameter name and hence...
Equivalent of eHive's Param module.
def has_param(self, param_name)
Returns a boolean.
def subst_one_hashpair(self, inside_hashes, is_expr)
Run the parameter substitution for a single pair of hashes.
def set_param(self, param_name, value)
Setter.
Used by Process.BaseRunnable.
Raised when the parameter name is not a string.
def param_substitute(self, structure)
Take any structure and replace the pairs of hashes with the values of the parameters / expression the...
Raised when parameters depend on each other, forming a loop.