text: from multiprocessing import Process, Queue
  from subprocess import check_output, STDOUT, CalledProcessError
  
  from ..utils import logutils
  
  
  log = logutils.get_logger(__name__)
  
  
  def loop_process(in_queue, out_queue):
      """
      Code to spawn a subprocess for running external tasks
  
      Parameters
      ----------
      in_queue : multiprocessing.Queue
          (add docstring)
      out_queue : multiprocessing.Queue
          (add docstring)
      """
      while True:
          try:
              cmd = in_queue.get()
  
              try:
                  result = check_output(cmd, stderr=STDOUT)
              except CalledProcessError as e:
                  result = e
              out_queue.put(result)
          except KeyboardInterrupt:
              continue
  
  
  class ETISubprocess:
      """
      A singleton class that creates an instance of __ETISubprocess, which
      any future instances of ETISubprocess will point to.
      """
      class __ETISubprocess:
          def __init__(self):
              self.inQueue = Queue()
              self.outQueue = Queue()
              self.process = Process(target=loop_process,
                                     args=(self.inQueue, self.outQueue))
              self.process.start()
  
          def terminate(self):
              self.process.terminate()
  
      instance = None
  
      def __new__(cls):
          if not ETISubprocess.instance:
              ETISubprocess.instance = ETISubprocess.__ETISubprocess()
          return ETISubprocess.instance
  
      def __getattr__(self, name):
          return getattr(self.instance, name)
  
      def __setattr__(self, name, value):
          return setattr(self.instance, name, value)
  
  
  class ExternalTaskInterface:
      """
      The External Task Interface base class. This is a way for the Recipe
      System to interact with ouside software. It prepares, executes, recovers,
      and cleans all files and parameters pertaining to any external task
      that interfaces with the recipe system.
  
      Parameters
      ----------
      primitives_class : (add input type)
          (add docstring)
      inputs : (add input type)
          (add docstring)
      params : (add input type)
          (add docstring)
      """
      param_objs = None
      file_objs = None
      inputs = None
      params = None
  
      def __init__(self, primitives_class=None, inputs=None, params=None):
          """
          :param rc: Used to store reduction information
          :type rc: ReductionContext
          """
          log.debug("ExternalTaskInterface __init__")
          self.inputs = inputs
          self.params = params
          self.param_objs = []
          self.file_objs = []
          self.inQueue = None
          self.outQueue = None
          try:
              self.inQueue = primitives_class.eti_subprocess.inQueue
              self.outQueue = primitives_class.eti_subprocess.outQueue
          except AttributeError:
              log.debug("ETI: Cannot access Queues")
              return
          if self.inQueue._closed or self.outQueue._closed:
              log.warning("ETI: One or both Queues is closed")
              self.inQueue = None
              self.outQueue = None
  
      def run(self):
          log.debug("ExternalTaskInterface.run()")
          self.prepare()
          self.execute()
          self.recover()
          self.clean()
  
      def add_param(self, param):
          log.debug("ExternalTaskInterface.add_param()")
          self.param_objs.append(param)
  
      def add_file(self, a_file):
          log.debug("ExternalTaskInterface.add_file()")
          self.file_objs.append(a_file)
  
      def prepare(self):
          log.debug("ExternalTaskInterface.prepare()")
          for par in self.param_objs:
              par.prepare()
          for fil in self.file_objs:
              fil.prepare()
  
      def execute(self):
          log.debug("ExternalTaskInterface.execute()")
  
      def recover(self):
          log.debug("ExternalTaskInterface.recover()")
          for par in self.param_objs:
              par.recover()
          for fil in self.file_objs:
              fil.recover()
  
      def clean(self):
          log.debug("ExternalTaskInterface.clean()")
          for par in self.param_objs:
              par.clean()
          for fil in self.file_objs:
              fil.clean()
  