def sprint_error_report(error_text="\n"): r""" Print a standardized error report that robot programs should print on failure. The report includes useful information like error text, command line, pid, userid, program parameters, etc. Callers must have declared a @{parm_list} variable which contains the names of all program parameters. """ # This function is deprecated. The caller is advised to call the # gen_print version of this function directly. return gp.sprint_error_report(error_text)
def rvalid_range(var_name, range): r""" Validate that a robot integer is within the given range. This function is the robot wrapper for gen_valid.svalid_range. Description of arguments: var_name The name of the variable whose value is to be validated. range A list comprised of one or two elements which are the lower and upper ends of a range. These values must be integers except where noted. Valid specifications may be of the following forms: [lower, upper], [lower] or [None, upper]. The caller may also specify this value as a string which will then be converted to a list in the aforementioned format: lower..upper, lower.. or ..upper. Examples of robot calls and corresponding output: Robot code... Rvalid Range MY_PARM 5..9 Output... #(CDT) 2018/05/09 11:45:00.166344 - 0.004252 - **ERROR** The following # variable is not within the expected range: MY_PARM: 4 valid_range: 5..9 """ var_value = BuiltIn().get_variable_value("${" + var_name + "}") if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: try: range = range.split("..") except AttributeError: pass if range[0] == "": range[0] = None range = [x for x in range if x] error_message = gv.svalid_range(var_value, range, var_name) if not error_message == "": error_message = gp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def process_error_message(error_message): r""" Process an error message. If error_message is non-blank, fail. Otherwise, do nothing. This function is designed solely for use by other functions in this file. Description of argument(s): error_message The error message to be processed. """ if error_message: error_message = gp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def rvalid_integer(var_name): r""" Validate a robot integer. This function is the robot wrapper for gen_valid.svalid_integer. Description of arguments: var_name The name of the variable whose value is to be validated. Examples of robot calls and corresponding output: Robot code... Rvalid Integer MY_PARM Output... #(CDT) 2016/11/02 10:44:43 - **ERROR** Variable "MY_PARM" not found (i.e. #it's undefined). or if it is defined but blank: Output... #(CDT) 2016/11/02 10:45:37 - **ERROR** Invalid integer value: MY_PARM: <blank> Robot code... ${MY_PARM}= Set Variable HELLO Rvalid Integer MY_PARM Output... #(CDT) 2016/11/02 10:46:18 - **ERROR** Invalid integer value: MY_PARM: HELLO """ # Note: get_variable_value() seems to have no trouble with local variables. var_value = BuiltIn().get_variable_value("${" + var_name + "}") if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: error_message = gv.svalid_integer(var_value, var_name) if not error_message == "": error_message = gp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def rvalid_value(var_name, invalid_values=[], valid_values=[]): r""" Validate a robot value. This function is the robot wrapper for gen_valid.svalid_value. Description of arguments: var_name The name of the variable whose value is to be validated. invalid_values A list of invalid values. If var_value is equal to any of these, it is invalid. Note that if you specify anything for invalid_values (below), the valid_values list is not even processed. valid_values A list of invalid values. var_value must be equal to one of these values to be considered valid. If either the invalid_values or the valid_values parms are not of type "list", they will be processed as python code in order to generate a list. This allows the robot programmer to essentially specify a list literal. For example, the robot code could contain the following: Rvalid Value var1 valid_values=['one', 'two'] Examples of robot calls and corresponding output: Robot code... rvalid_value MY_PARM Output... #(CDT) 2016/11/02 10:04:20 - **ERROR** Variable "MY_PARM" not found (i.e. #it's undefined). or if it is defined but blank: Output... #(CDT) 2016/11/02 10:14:24 - **ERROR** The following variable has an #invalid value: MY_PARM: It must NOT be one of the following values: invalid_values: invalid_values[0]: <blank> Robot code... ${invalid_values}= Create List one two three ${MY_PARM}= Set Variable one rvalid_value MY_PARM invalid_values=${invalid_values} Output... #(CDT) 2016/11/02 10:20:05 - **ERROR** The following variable has an #invalid value: MY_PARM: one It must NOT be one of the following values: invalid_values: invalid_values[0]: one invalid_values[1]: two invalid_values[2]: three """ # Note: get_variable_value() seems to have no trouble with local variables. var_value = BuiltIn().get_variable_value("${" + var_name + "}") if type(valid_values) is not list: # Evaluate python syntax to convert to a list. exec("valid_values = " + valid_values) if type(invalid_values) is not list: # Evaluate python syntax to convert to a list. exec("invalid_values = " + invalid_values) if var_value is None: var_value = "" error_message = "Variable \"" + var_name +\ "\" not found (i.e. it's undefined).\n" else: error_message = gv.svalid_value(var_value, invalid_values, valid_values, var_name) if not error_message == "": error_message = gp.sprint_error_report(error_message) BuiltIn().fail(error_message)
def set_ffdc_defaults(ffdc_dir_path=None, ffdc_prefix=None): r""" Set a default value for ffdc_dir_path and ffdc_prefix if they don't already have values. Return both values. Description of arguments: ffdc_dir_path The dir path where FFDC data should be put. ffdc_prefix The prefix to be given to each FFDC file name generated. NOTE: If global variable ffdc_dir_path_style is set to ${1}, this function will create default values in a newer way. Otherwise, its behavior will remain unchanged. """ # Note: Several subordinate functions like 'Get Test Dir and Name' and # 'Header Message' expect global variable FFDC_TIME to be set. cmd_buf = ["Get Current Time Stamp"] gp.dprint_issuing(cmd_buf) FFDC_TIME = BuiltIn().run_keyword(*cmd_buf) BuiltIn().set_global_variable("${FFDC_TIME}", FFDC_TIME) ffdc_dir_path_style = BuiltIn().get_variable_value( "${ffdc_dir_path_style}") if ffdc_dir_path is None: if ffdc_dir_path_style: try: ffdc_dir_path = os.environ['FFDC_DIR_PATH'] except KeyError: ffdc_dir_path = os.path.dirname( BuiltIn().get_variable_value("${LOG_FILE}")) + "/" else: FFDC_LOG_PATH = os.getcwd() + "/logs/" if FFDC_LOG_PATH is None: FFDC_LOG_PATH = "" if FFDC_LOG_PATH == "": FFDC_LOG_PATH = os.path.dirname( BuiltIn().get_variable_value("${LOG_FILE}")) + "/" error_message = gv.valid_value(FFDC_LOG_PATH, var_name="FFDC_LOG_PATH") if error_message != "": error_message = gp.sprint_error_report(error_message) BuiltIn().fail(error_message) FFDC_LOG_PATH = os.path.normpath(FFDC_LOG_PATH) + os.sep cmd_buf = ["Get Test Dir and Name"] gp.print_issuing(cmd_buf) suitename, testname = BuiltIn().run_keyword(*cmd_buf) ffdc_dir_path = FFDC_LOG_PATH + suitename + "/" + testname + "/" # Add trailing slash. ffdc_dir_path = os.path.normpath(ffdc_dir_path) + os.sep if ffdc_prefix is None: FFDC_TIME = BuiltIn().get_variable_value("${FFDC_TIME}") if ffdc_prefix is None: if ffdc_dir_path_style: OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}") OPENBMC_NICKNAME = BuiltIn().get_variable_value( "${OPENBMC_NICKNAME}", default=OPENBMC_HOST) ffdc_prefix = OPENBMC_NICKNAME + "." + FFDC_TIME[2:8] + "." +\ FFDC_TIME[8:14] + "." else: ffdc_prefix = FFDC_TIME + "_" BuiltIn().set_global_variable("${FFDC_DIR_PATH}", ffdc_dir_path) BuiltIn().set_global_variable("${FFDC_PREFIX}", ffdc_prefix) return ffdc_dir_path, ffdc_prefix
def ffdc(ffdc_dir_path=None, ffdc_prefix=None, ffdc_function_list=""): r""" Gather First Failure Data Capture (FFDC). This includes: - Set global FFDC_TIME. - Create FFDC work space directory. - Write test info details. - Call BMC methods to write/collect FFDC data. Description of arguments: ffdc_dir_path The dir path where FFDC data should be put. ffdc_prefix The prefix to be given to each FFDC file name generated. ffdc_function_list A colon-delimited list of all the types of FFDC data you wish to have collected. A blank value means that all possible kinds of FFDC are to be collected. See FFDC_METHOD_CALL object in lib/openbmc_ffdc_list.py for possible choices. """ ffdc_file_list = [] # Check if Ping and SSH connection is alive OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}") state = st.get_state(req_states=['ping', 'uptime', 'rest']) gp.qprint_var(state) if not int(state['ping']): gp.print_error("BMC is not ping-able. Terminating FFDC collection.\n") return ffdc_file_list if not int(state['rest']): gp.print_error("REST commands to the BMC are failing." + " Terminating FFDC collection.\n") return ffdc_file_list if state['uptime'] == "": gp.print_error("BMC is not communicating via ssh. Terminating FFDC" + " collection.\n") return ffdc_file_list gp.qprint_timen("Collecting FFDC.") # Get default values for arguments. ffdc_dir_path, ffdc_prefix = set_ffdc_defaults(ffdc_dir_path, ffdc_prefix) gp.qprint_var(ffdc_dir_path) gp.qprint_var(ffdc_prefix) # LOG_PREFIX is used by subordinate functions. LOG_PREFIX = ffdc_dir_path + ffdc_prefix BuiltIn().set_global_variable("${LOG_PREFIX}", LOG_PREFIX) cmd_buf = ["Create Directory", ffdc_dir_path] gp.qprint_issuing(cmd_buf) status, output = BuiltIn().run_keyword_and_ignore_error(*cmd_buf) if status != "PASS": error_message = gp.sprint_error_report("Create Directory failed" + " with the following" + " error:\n" + output) BuiltIn().fail(error_message) # FFDC_FILE_PATH is used by Header Message. FFDC_FILE_PATH = ffdc_dir_path + ffdc_prefix + "BMC_general.txt" BuiltIn().set_global_variable("${FFDC_FILE_PATH}", FFDC_FILE_PATH) status, ffdc_file_list = grk.run_key_u("Header Message") status, ffdc_file_sub_list = \ grk.run_key_u("Call FFDC Methods ffdc_function_list=" + ffdc_function_list) # Combine lists, remove duplicates and sort. ffdc_file_list = sorted(set(ffdc_file_list + ffdc_file_sub_list)) gp.qprint_timen("Finished collecting FFDC.") return ffdc_file_list
def shell_cmd(command_string, quiet=None, print_output=None, show_err=1, test_mode=0, time_out=None, max_attempts=1, retry_sleep_time=5, allowed_shell_rcs=[0], ignore_err=None, return_stderr=0, fork=0): r""" Run the given command string in a shell and return a tuple consisting of the shell return code and the output. Description of argument(s): command_string The command string to be run in a shell (e.g. "ls /tmp"). quiet If set to 0, this function will print "Issuing: <cmd string>" to stdout. When the quiet argument is set to None, this function will assign a default value by searching upward in the stack for the quiet variable value. If no such value is found, quiet is set to 0. print_output If this is set, this function will print the stdout/stderr generated by the shell command to stdout. show_err If show_err is set, this function will print a standardized error report if the shell command fails (i.e. if the shell command returns a shell_rc that is not in allowed_shell_rcs). Note: Error text is only printed if ALL attempts to run the command_string fail. In other words, if the command execution is ultimately successful, initial failures are hidden. test_mode If test_mode is set, this function will not actually run the command. If print_output is also set, this function will print "(test_mode) Issuing: <cmd string>" to stdout. A caller should call shell_cmd directly if they wish to have the command string run unconditionally. They should call the t_shell_cmd wrapper (defined below) if they wish to run the command string only if the prevailing test_mode variable is set to 0. time_out A time-out value expressed in seconds. If the command string has not finished executing within <time_out> seconds, it will be halted and counted as an error. max_attempts The max number of attempts that should be made to run the command string. retry_sleep_time The number of seconds to sleep between attempts. allowed_shell_rcs A list of integers indicating which shell_rc values are not to be considered errors. ignore_err Ignore error means that a failure encountered by running the command string will not be raised as a python exception. When the ignore_err argument is set to None, this function will assign a default value by searching upward in the stack for the ignore_err variable value. If no such value is found, ignore_err is set to 1. return_stderr If return_stderr is set, this function will process the stdout and stderr streams from the shell command separately. In such a case, the tuple returned by this function will consist of three values rather than just two: rc, stdout, stderr. fork Run the command string asynchronously (i.e. don't wait for status of the child process and don't try to get stdout/stderr). """ # Assign default values to some of the arguments to this function. quiet = int(gm.dft(quiet, gp.get_stack_var('quiet', 0))) print_output = int(gm.dft(print_output, not quiet)) show_err = int(show_err) global_ignore_err = gp.get_var_value(ignore_err, 1) stack_ignore_err = gp.get_stack_var('ignore_err', global_ignore_err) ignore_err = int(gm.dft(ignore_err, gm.dft(stack_ignore_err, 1))) err_msg = gv.valid_value(command_string) if err_msg != "": raise ValueError(err_msg) if not quiet: gp.print_issuing(command_string, test_mode) if test_mode: if return_stderr: return 0, "", "" else: return 0, "" # Convert each list entry to a signed value. allowed_shell_rcs = fa.source_to_object(allowed_shell_rcs) allowed_shell_rcs = [gm.to_signed(x) for x in allowed_shell_rcs] if return_stderr: stderr = subprocess.PIPE else: stderr = subprocess.STDOUT shell_rc = 0 out_buf = "" err_buf = "" # Write all output to func_history_stdout rather than directly to stdout. # This allows us to decide what to print after all attempts to run the # command string have been made. func_history_stdout will contain the # complete stdout history from the current invocation of this function. func_history_stdout = "" for attempt_num in range(1, max_attempts + 1): sub_proc = subprocess.Popen(command_string, bufsize=1, shell=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=stderr) out_buf = "" err_buf = "" # Output from this loop iteration is written to func_stdout for later # processing. func_stdout = "" if fork: break command_timed_out = False if time_out is not None: # Designate a SIGALRM handling function and set alarm. signal.signal(signal.SIGALRM, shell_cmd_timed_out) signal.alarm(time_out) try: if return_stderr: for line in sub_proc.stderr: try: err_buf += line except TypeError: line = line.decode("utf-8") err_buf += line if not print_output: continue func_stdout += line for line in sub_proc.stdout: try: out_buf += line except TypeError: line = line.decode("utf-8") out_buf += line if not print_output: continue func_stdout += line except IOError: command_timed_out = True sub_proc.communicate() shell_rc = sub_proc.returncode # Restore the original SIGALRM handler and clear the alarm. signal.signal(signal.SIGALRM, original_sigalrm_handler) signal.alarm(0) if shell_rc in allowed_shell_rcs: break err_msg = "The prior shell command failed.\n" if quiet: err_msg += gp.sprint_var(command_string) if command_timed_out: err_msg += gp.sprint_var(command_timed_out) err_msg += gp.sprint_var(time_out) err_msg += gp.sprint_varx("child_pid", sub_proc.pid) err_msg += gp.sprint_var(attempt_num) err_msg += gp.sprint_var(shell_rc, gp.hexa()) err_msg += gp.sprint_var(allowed_shell_rcs, gp.hexa()) if not print_output: if return_stderr: err_msg += "err_buf:\n" + err_buf err_msg += "out_buf:\n" + out_buf if show_err: func_stdout += gp.sprint_error_report(err_msg) func_history_stdout += func_stdout if attempt_num < max_attempts: func_history_stdout += gp.sprint_issuing("time.sleep(" + str(retry_sleep_time) + ")") time.sleep(retry_sleep_time) if shell_rc not in allowed_shell_rcs: func_stdout = func_history_stdout gp.gp_print(func_stdout) if shell_rc not in allowed_shell_rcs: if not ignore_err: if robot_env: BuiltIn().fail(err_msg) else: raise ValueError("The prior shell command failed.\n") if return_stderr: return shell_rc, out_buf, err_buf else: return shell_rc, out_buf
def run(self, func, *args, **kwargs): r""" Run the indicated function with the given args and kwargs and return the value that the function returns. If the time_out value expires, raise a ValueError exception with a detailed error message. This method passes all of the args and kwargs directly to the child function with the following important exception: If kwargs contains a 'time_out' value, it will be used to set the func timer object's time_out value and then the kwargs['time_out'] entry will be removed. If the time-out expires before the function finishes running, this method will raise a ValueError. Example: func_timer = func_timer_class() func_timer.run(run_key, "sleep 3", time_out=2) Example: try: result = func_timer.run(func1, "parm1", time_out=2) print_var(result) except ValueError: print("The func timed out but we're handling it.") Description of argument(s): func The function object which is to be called. args The arguments which are to be passed to the function object. kwargs The keyword arguments which are to be passed to the function object. As noted above, kwargs['time_out'] will get special treatment. """ gp.lprint_executing() # Store method parms as object parms. self.__func = func # Get self.__time_out value from kwargs. If kwargs['time_out'] is # not present, self.__time_out will default to None. self.__time_out = None if 'time_out' in kwargs: self.__time_out = kwargs['time_out'] del kwargs['time_out'] # Convert "none" string to None. try: if self.__time_out.lower() == "none": self.__time_out = None except AttributeError: pass if self.__time_out is not None: self.__time_out = int(self.__time_out) # Ensure that time_out is non-negative. message = gv.svalid_range(self.__time_out, [0], "time_out") if message != "": raise ValueError("\n" + gp.sprint_error_report(message, format='long')) gp.lprint_varx("time_out", self.__time_out) self.__child_pid = 0 if self.__time_out is not None: # Save the original SIGUSR1 handler for later restoration by this # class' methods. self.__original_SIGUSR1_handler = signal.getsignal(signal.SIGUSR1) # Designate a SIGUSR1 handling function. signal.signal(signal.SIGUSR1, self.timed_out) parent_pid = os.getpid() self.__child_pid = os.fork() if self.__child_pid == 0: gp.dprint_timen("Child timer pid " + str(os.getpid()) + ": Sleeping for " + str(self.__time_out) + " seconds.") time.sleep(self.__time_out) gp.dprint_timen("Child timer pid " + str(os.getpid()) + ": Sending SIGUSR1 to parent pid " + str(parent_pid) + ".") os.kill(parent_pid, signal.SIGUSR1) os._exit(0) # Call the user's function with the user's arguments. children = gm.get_child_pids() gp.lprint_var(children) gp.lprint_timen("Calling the user's function.") gp.lprint_varx("func_name", func.__name__) gp.lprint_vars(args, kwargs) try: result = func(*args, **kwargs) except Exception as func_exception: # We must handle all exceptions so that we have the chance to # cleanup before re-raising the exception. gp.lprint_timen("Encountered exception in user's function.") self.cleanup() raise(func_exception) gp.lprint_timen("Returned from the user's function.") self.cleanup() return result
def shell_cmd(command_string, quiet=None, print_output=None, show_err=1, test_mode=0, time_out=None, max_attempts=1, retry_sleep_time=5, valid_rcs=[0], ignore_err=None, return_stderr=0, fork=0, error_regexes=None): r""" Run the given command string in a shell and return a tuple consisting of the shell return code and the output. Description of argument(s): command_string The command string to be run in a shell (e.g. "ls /tmp"). quiet If set to 0, this function will print "Issuing: <cmd string>" to stdout. When the quiet argument is set to None, this function will assign a default value by searching upward in the stack for the quiet variable value. If no such value is found, quiet is set to 0. print_output If this is set, this function will print the stdout/stderr generated by the shell command to stdout. show_err If show_err is set, this function will print a standardized error report if the shell command fails (i.e. if the shell command returns a shell_rc that is not in valid_rcs). Note: Error text is only printed if ALL attempts to run the command_string fail. In other words, if the command execution is ultimately successful, initial failures are hidden. test_mode If test_mode is set, this function will not actually run the command. If print_output is also set, this function will print "(test_mode) Issuing: <cmd string>" to stdout. A caller should call shell_cmd directly if they wish to have the command string run unconditionally. They should call the t_shell_cmd wrapper (defined below) if they wish to run the command string only if the prevailing test_mode variable is set to 0. time_out A time-out value expressed in seconds. If the command string has not finished executing within <time_out> seconds, it will be halted and counted as an error. max_attempts The max number of attempts that should be made to run the command string. retry_sleep_time The number of seconds to sleep between attempts. valid_rcs A list of integers indicating which shell_rc values are not to be considered errors. ignore_err Ignore error means that a failure encountered by running the command string will not be raised as a python exception. When the ignore_err argument is set to None, this function will assign a default value by searching upward in the stack for the ignore_err variable value. If no such value is found, ignore_err is set to 1. return_stderr If return_stderr is set, this function will process the stdout and stderr streams from the shell command separately. In such a case, the tuple returned by this function will consist of three values rather than just two: rc, stdout, stderr. fork Run the command string asynchronously (i.e. don't wait for status of the child process and don't try to get stdout/stderr) and return the Popen object created by the subprocess.popen() function. See the kill_cmd function for details on how to process the popen object. error_regexes A list of regular expressions to be used to identify errors in the command output. If there is a match for any of these regular expressions, the command will be considered a failure and the shell_rc will be set to -1. For example, if error_regexes = ['ERROR:'] and the command output contains 'ERROR: Unrecognized option', it will be counted as an error even if the command returned 0. This is useful when running commands that do not always return non-zero on error. """ err_msg = gv.valid_value(command_string) if err_msg: raise ValueError(err_msg) # Assign default values to some of the arguments to this function. quiet = int(gm.dft(quiet, gp.get_stack_var('quiet', 0))) print_output = int(gm.dft(print_output, not quiet)) show_err = int(show_err) ignore_err = int(gm.dft(ignore_err, gp.get_stack_var('ignore_err', 1))) gp.qprint_issuing(command_string, test_mode) if test_mode: return (0, "", "") if return_stderr else (0, "") # Convert a string python dictionary definition to a dictionary. valid_rcs = fa.source_to_object(valid_rcs) # Convert each list entry to a signed value. valid_rcs = [gm.to_signed(x) for x in valid_rcs] stderr = subprocess.PIPE if return_stderr else subprocess.STDOUT # Write all output to func_out_history_buf rather than directly to stdout. This allows us to decide # what to print after all attempts to run the command string have been made. func_out_history_buf will # contain the complete history from the current invocation of this function. global command_timed_out command_timed_out = False func_out_history_buf = "" for attempt_num in range(1, max_attempts + 1): sub_proc = subprocess.Popen(command_string, preexec_fn=os.setsid, bufsize=1, shell=True, universal_newlines=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=stderr) if fork: return sub_proc if time_out: command_timed_out = False # Designate a SIGALRM handling function and set alarm. signal.signal(signal.SIGALRM, shell_cmd_timed_out) signal.alarm(time_out) try: stdout_buf, stderr_buf = sub_proc.communicate() except IOError: command_timed_out = True # Restore the original SIGALRM handler and clear the alarm. signal.signal(signal.SIGALRM, original_sigalrm_handler) signal.alarm(0) # Output from this loop iteration is written to func_out_buf for later processing. This can include # stdout, stderr and our own error messages. func_out_buf = "" if print_output: if return_stderr: func_out_buf += stderr_buf func_out_buf += stdout_buf shell_rc = sub_proc.returncode if shell_rc in valid_rcs: # Check output for text indicating there is an error. if error_regexes and re.match('|'.join(error_regexes), stdout_buf): shell_rc = -1 else: break err_msg = "The prior shell command failed.\n" err_msg += gp.sprint_var(attempt_num) err_msg += gp.sprint_vars(command_string, command_timed_out, time_out) err_msg += gp.sprint_varx("child_pid", sub_proc.pid) err_msg += gp.sprint_vars(shell_rc, valid_rcs, fmt=gp.hexa()) if error_regexes: err_msg += gp.sprint_vars(error_regexes) if not print_output: if return_stderr: err_msg += "stderr_buf:\n" + stderr_buf err_msg += "stdout_buf:\n" + stdout_buf if show_err: func_out_buf += gp.sprint_error_report(err_msg) if attempt_num < max_attempts: cmd_buf = "time.sleep(" + str(retry_sleep_time) + ")" if show_err: func_out_buf += gp.sprint_issuing(cmd_buf) exec(cmd_buf) func_out_history_buf += func_out_buf if shell_rc in valid_rcs: gp.gp_print(func_out_buf) else: if show_err: gp.gp_print(func_out_history_buf, stream='stderr') else: # There is no error information to show so just print output from last loop iteration. gp.gp_print(func_out_buf) if not ignore_err: # If the caller has already asked to show error info, avoid repeating that in the failure message. err_msg = "The prior shell command failed.\n" if show_err \ else err_msg if robot_env: BuiltIn().fail(err_msg) else: raise ValueError(err_msg) return (shell_rc, stdout_buf, stderr_buf) if return_stderr \ else (shell_rc, stdout_buf)