def exec_while(condition, inner_while): """ Generate the Argo recursive logic. For example https://github.com/argoproj/argo/blob/master/examples/README.md#recursion. """ # _while_lock means 'exec_while' operation begins to work # _while_steps stores logic steps inside the recursion logic states._while_lock = True # Enforce inner function of the while-loop to run if callable(inner_while): branch = inner_while() if branch is None: raise SyntaxError("require function return value") else: raise TypeError("condition to run would be a function") branch_dict = output.extract_step_return(branch) recursive_name = "exec-while-" + branch_dict["name"] recursive_id = "exec-while-" + branch_dict["id"] if states.workflow.get_template(recursive_name) is None: template = Steps(name=recursive_name) else: raise SyntaxError("Recursive function can not be called twice ") # Generate leaving point for recursive step_out_name = "%s-%s" % (recursive_name, "exit") pre = condition["pre"] pre_dict = output.extract_step_return(pre) condition_suffix = condition["condition"] # Generate the recursive go to step when_prefix = "{{steps.%s.%s}} %s %s" % ( branch_dict["id"], branch_dict["output"], condition_suffix, pre_dict["value"], ) step_out_template = OrderedDict({ "name": step_out_name, "template": recursive_name, "when": when_prefix, }) step_out_id = utils.invocation_name(step_out_name, recursive_id) states._while_steps[step_out_id] = [step_out_template] # Add steps inside the recursive logic to recursive template template.steps = list(states._while_steps.values()) # Add this recursive logic to the templates states.workflow.add_template(template) # Add recursive logic to global _steps recursive_out_step = Step(name=recursive_id, template=recursive_name) states.workflow.add_step(name=recursive_id, step=recursive_out_step) states._while_lock = False states._while_steps = OrderedDict()
def when(condition, function): """Generates an Argo conditional step. For example, the coinflip example in https://github.com/argoproj/argo/blob/master/examples/coinflip.yaml. """ pre = condition["pre"] post = condition["post"] if pre is None or post is None: raise SyntaxError("Output of function can not be null") condition_suffix = condition["condition"] pre_dict = output.extract_step_return(pre) post_dict = output.extract_step_return(post) if "name" in pre_dict: left_function_id = pre_dict["id"] if states.workflow.get_step(left_function_id) is None: states.workflow.add_step( name=left_function_id, step=Step(name=left_function_id, template=pre_dict["name"]), ) else: # TODO: fixed if left branch is a variable rather than function pre_dict["value"] post_value = post_dict["value"] if states._upstream_dag_task is not None: step_type = "tasks" states._when_task = pre_dict["id"] else: step_type = "steps" states._when_prefix = "{{%s.%s.%s}} %s %s" % ( step_type, pre_dict["id"], pre_dict["output"], condition_suffix, post_value, ) states._condition_id = "%s.%s" % (pre_dict["id"], pre_dict["output"]) # Enforce the function to run and lock to add into step if callable(function): function() else: raise TypeError("condition to run would be a function") states._when_prefix = None states._condition_id = None
def map(function, input_list): """ map operation of Couler """ # Enforce the function to run and lock to add into step if callable(function): states._update_steps_lock = False # TODO (terrytangyuan): Support functions with multiple arguments. para = input_list[0] inner = function(para) if inner is None: raise SyntaxError("require function return value") states._update_steps_lock = True else: raise TypeError("require loop over a function to run") inner_dict = output.extract_step_return(inner) template_name = inner_dict["name"] inner_step = Step(name=inner_dict["id"], template=template_name) parameters = [] items_param_name = "%s-para-name" % template_name items_param_dict = {"name": items_param_name} function_template = states.workflow.get_template(template_name) function_template_dict = function_template.to_dict() if "resource" in function_template_dict: # Update the template with the new dynamic `metadata.name`. manifest_dict = yaml.safe_load( function_template_dict["resource"]["manifest"] ) manifest_dict["metadata"]["name"] = ( "'{{inputs.parameters.%s}}'" % items_param_name ) function_template = states.workflow.get_template(template_name) function_template.manifest = pyaml.dump(manifest_dict) # Append this items parameter to input parameters in the template function_template.args.append(items_param_dict) states.workflow.add_template(function_template) input_parameters = [items_param_dict] else: input_parameters = function_template_dict["inputs"]["parameters"] for para_name in input_parameters: parameters.append( { "name": para_name["name"], "value": '"{{item.%s}}"' % para_name["name"], } ) inner_step.arguments = {"parameters": parameters} with_items = [] for para_values in input_list: item = {} if not isinstance(para_values, list): para_values = [para_values] for j in range(len(input_parameters)): para_name = input_parameters[j]["name"] item[para_name] = para_values[j] with_items.append(item) inner_step.with_items = with_items states.workflow.add_step(inner_dict["id"], inner_step) return inner_step
def map(function, *arguments): """ map operation of Couler """ # Enforce the function to run and lock to add into step # Checks the correct syntax if callable(function): states._update_steps_lock = False para = [] x = 0 while x < len(arguments): para.append(arguments[x][0]) x += 1 inner = function(*para) if inner is None: raise SyntaxError("require function return value") states._update_steps_lock = True else: raise TypeError("require loop over a function to run") inner_dict = output.extract_step_return(inner) template_name = inner_dict["name"] inner_step = Step(name=inner_dict["id"], template=template_name) parameters = [] items_param_name = "%s-para-name" % template_name items_param_dict = {"name": items_param_name} function_template = states.workflow.get_template(template_name) function_template_dict = function_template.to_dict() if "resource" in function_template_dict: # Update the template with the new dynamic `metadata.name`. manifest_dict = yaml.safe_load( function_template_dict["resource"]["manifest"]) manifest_dict["metadata"]["name"] = ("'{{inputs.parameters.%s}}'" % items_param_name) function_template = states.workflow.get_template(template_name) function_template.manifest = pyaml.dump(manifest_dict) # Append this items parameter to input parameters in the template function_template.args.append(items_param_dict) states.workflow.add_template(function_template) input_parameters = [items_param_dict] else: input_parameters = function_template_dict["inputs"]["parameters"] for para_name in input_parameters: parameters.append({ "name": para_name["name"], "value": '"{{item.%s}}"' % para_name["name"], }) inner_step.arguments = {"parameters": parameters} # the following part of the code # Adds values to parameters (with items) while it goes # through the *arguments-variable with two loops. # inner loop: # arguments[ind_of_func_param][0], arguments[ind_of_func_param][0]... # Outer loop: # arguments[0][ind_of_func_call], arguments[0][ind_of_func_call]... # With two lists in *arguments # result would be: # 1. pair of items for the function: # arguments[0][0], arguments[1][0] # 2. pair of items for the function: # arguments[0][1], arguments[1][1] # and so on... with_items = [] ind_of_func_call = 0 # the number of calls to be made to function while ind_of_func_call < len(arguments[0]): ind_of_func_param = 0 item = {} # the number of parameters function takes. while ind_of_func_param < len(arguments): # checks if arguments[ind_of_func_param] is a list if not makes it if not isinstance(arguments[ind_of_func_param], list): arguments[ind_of_func_param] = [arguments[ind_of_func_param]] # items are created for the with items part in .yaml para_name = input_parameters[ind_of_func_param]["name"] item[para_name] = arguments[ind_of_func_param][ind_of_func_call] ind_of_func_param += 1 with_items.append(item) ind_of_func_call += 1 # all the created items are added to the step and # then the step is added to .yaml inner_step.with_items = with_items states.workflow.add_step(inner_dict["id"], inner_step) return inner_step