text: # This module contains code that manage processes.
  
  from kbase import * 
  from kout import *
  import subprocess
  from subprocess import *
  
  # Helper function for get_cmd_output() and show_cmd_output(). Join a list of
  # strings with whitespaces.
  def krun_join_with_whitespace(l):
      res = ""
      for e in l:
          if res != "": res += " "
          res += e
      return res
  
  # Helper function for get_cmd_output() and show_cmd_output(). Return a list of
  # strings or a single string depending on the value of 'shell_flag'.
  def krun_adjust_arg_list(arg_list, shell_flag):
      args = arg_list
      if shell_flag and type(arg_list) == list: args = krun_join_with_whitespace(arg_list)
      elif not shell_flag and type(arg_list) == str: args = arg_list.split()
      return args
  
  # This function executes the command specified and returns the standard output
  # of the command.
  # 
  # By default, the function does not execute the command in the context of a
  # shell. This can be overriden by setting 'shell_flag' to true.
  # 
  # The first argument expected by the function is a list of strings or a single
  # string. If a single string is provided and the command is not executed in the
  # context of a shell, the string is split at whitespaces to obtain the list of
  # arguments. Conversely, if a list of strings is provided and the command is
  # executed in the context of a shell, the list of strings is joined with
  # whitespaces.
  # 
  # The 'err_behavior' value controls the behavior of the function when an error
  # occurs. If 'err_behavior' is set to 'brief', an exception is thrown containing
  # the error string generated by the command. If 'err_behavior' is set to 'full',
  # an exception is thrown containing both the text of the command and the error
  # string generated by the command. If 'err_behavior' is set to 'ignore', no
  # exception is thrown. By default, 'err_behavior' is set to 'brief'.
  # 
  # If 'input_str' is non-null, it is written to the standard input of the
  # process.
  def get_cmd_output(arg_list, err_behavior="brief", shell_flag=0, input_str=None):
      args = krun_adjust_arg_list(arg_list, shell_flag)
      
      try:
          stdin = None
          if input_str != None: stdin=PIPE
  	proc = Popen(args=args, stdin=stdin, stdout=PIPE, stderr=PIPE, shell=shell_flag)
  	(out_text, err_text) = proc.communicate(input_str)
  	
          # An error occurred.
  	if proc.returncode != 0 and err_behavior != "ignore":
              
              # Strip the surrounding whitespaces and the trailing '.' of both
              # streams.
              out_text = out_text.strip().rstrip('.')
              err_text = err_text.strip().rstrip('.')
              
              # If err_text is empty, use out_text if not empty, otherwise use
              # 'unknown error'.
              if len(err_text): msg = err_text
              elif len(out_text): msg = out_text
              else: msg = 'unknown error'
              
              raise Exception(msg)
  	
          return out_text
  	
      except Exception, e:
          
          # We're ignoring errors and it seems the command could not be executed.
          # Return an empty string.
          if err_behavior == "ignore": return ""
          
          err_msg = str(e)
          if err_behavior == "full":
              if type(args) == list: cmd_text = krun_join_with_whitespace(args)
              else: cmd_text = args
              err_msg = "command '%s' failed: %s" % (cmd_text, err_msg)
          raise Exception(err_msg)
  
  # This function is similar to get_cmd_output(), with the difference that the
  # output of the command (stdout, stderr) is not redirected. The function throws
  # an exception if the command fails if requested.
  def show_cmd_output(arg_list, ignore_error=0, shell_flag=0, input_str=None):
      args = krun_adjust_arg_list(arg_list, shell_flag)
      
      if type(arg_list) == str: cmd_name = arg_list.split()[0]
      else: cmd_name = arg_list[0]
      
      try:
          # Flush stdout and stderr since Python is buffering those streams and
          # the Popen() call is going to write to those streams directly,
          # resulting in out-of-order output when the streams are not redirected
          # to a terminal.
          sys.stdout.flush()
          sys.stderr.flush()
          
          stdin = None
          if input_str != None: stdin=PIPE
  	proc = Popen(args=args, stdin=stdin, shell=shell_flag)
  	proc.communicate(input_str)
  	if proc.returncode != 0 and not ignore_error: raise Exception("command " + cmd_name + " failed")
  	
      except Exception, e:
          if not ignore_error: raise Exception("command " + cmd_name + " failed")
  
  # This function returns an escaped and quoted shell argument.
  def escape_shell_arg(arg):
      if type(arg) ==  None or arg == "":
  	return "\'\'"
  
      arg = str(arg)
      arg = arg.replace("\'", "\'\"\'\"\'")
  
      return "\'" + arg + "\'"
      
  # Allows to run a program, with some data in stdin, return code, and debug
  # stdout and stderr. No stdin yet.
  # example usages:
  # KPopen(None, "/bin/true")
  # KPopen("", "ls", "-la")
  # KPopen("", ["ls", "-la"])
  # KPopen("hello", "cat")
  # returns an object with the following properties:
  #    command, return_code, stdin, stdout, stderr
  # on exception, there is debugging output
  # when no exception, you can call the debug_result method manually if you want some debugging info
  class KPopen:
      command = None
      proc = None
      stdout = None
      stderr = None
      return_code = None
  
      # if you want send EOF, set stdin_data to "" and not None
      def __init__(self, stdin_data, *cmd):
          self.stdin = stdin_data
          # accept an array or several parameters
          # this is ugly... 
          if type(cmd[0]) == list:
              cmd = cmd[0]
          try:
              self.command = cmd
              self.proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  
              self.stdout, self.stderr = self.proc.communicate(stdin_data)
              self.return_code = self.proc.returncode
          except OSError, e:
              self.debug_result() # if debugging is enabled, put debugging info to stderr or log (see kout and klog modules)
              raise
  
      def debug_result(self, func=None):
          f = debug
          if func:
              f = func
          f("command: %s" % (str(self.command)) )
          f("return code: %s" % (str(self.return_code)) )
          for line in self.stdin.split("\n"):
              f("stdin: " + line)
          for line in self.stdout.split("\n"):
              f("stdout: " + line)
          for line in self.stderr.split("\n"):
              f("stderr: " + line)
  
  ### BEGIN_TESTS ###
  
  def kpopen_test():
      do_debug()
      print "TEST 1"
      try:
          proc = KPopen("", "ls", "-la", "/etc/passwd")
      except Exception, e:
          print "ERROR: " + str(e)
      print
      print
      print "TEST 2"
      try:
          proc = KPopen("", "/bin/false")
      except Exception, e:
          print "ERROR: " + str(e)
      print
      print
      print "TEST 3"
      try:
          proc = KPopen("", ["ls", "-la", "not_existing_file"])
      except Exception, e:
          print "ERROR: " + str(e)
      print
      print
  
  if __name__ == "__main__":
      kpopen_test()
  
  ### END_TESTS ###
  
  