commit:     f195588254e4f1516ec024973ebba3660622c3fa
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Mon Dec 15 23:00:17 2014 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Mon Dec 15 23:00:17 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=f1955882

add subprocess helper module

---
 roverlay/tools/subproc.py | 157 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 157 insertions(+)

diff --git a/roverlay/tools/subproc.py b/roverlay/tools/subproc.py
new file mode 100644
index 0000000..a62601f
--- /dev/null
+++ b/roverlay/tools/subproc.py
@@ -0,0 +1,157 @@
+# R overlay -- tools, subprocess helpers
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 André Erdmann <d...@mailerd.de>
+# Distributed under the terms of the GNU General Public License;
+# either version 2 of the License, or (at your option) any later version.
+
+import os
+import subprocess
+import sys
+
+__all__ = [
+   'get_subproc_devnull',
+   'subproc_send_term', 'subproc_send_kill',
+   'stop_subprocess', 'gracefully_stop_subprocess',
+   'create_subprocess', 'run_subprocess'
+]
+
+# python >= 3.3 has ProcessLookupError, use more generic exception otherwise
+if sys.hexversion >= 0x3030000:
+   _ProcessLookupError = ProcessLookupError
+else:
+   _ProcessLookupError = OSError
+
+# python >= 3.3:
+if hasattr ( subprocess, 'DEVNULL' ):
+   def get_subproc_devnull(mode=None):
+      """Returns a devnull object suitable
+      for passing it as stdin/stdout/stderr to subprocess.Popen().
+
+      Python 3.3 and later variant: uses subprocess.DEVNULL
+
+      arguments:
+      * mode -- ignored
+      """
+      return subprocess.DEVNULL
+else:
+   def get_subproc_devnull(mode='a+'):
+      """Returns a devnull object suitable
+      for passing it as stdin/stdout/stderr to subprocess.Popen().
+
+      Python 3.2 and earlier variant: opens os.devnull
+
+      arguments:
+      * mode -- mode for open(). Defaults to read/append
+      """
+      return open ( os.devnull, mode )
+# --
+
+def _proc_catch_lookup_err ( func ):
+   def wrapped ( proc ):
+      try:
+         func ( proc )
+      except _ProcessLookupError:
+         return False
+      return True
+
+   return wrapped
+
+@_proc_catch_lookup_err
+def subproc_send_term ( proc ):
+   proc.terminate()
+
+@_proc_catch_lookup_err
+def subproc_send_kill ( proc ):
+   proc.kill()
+
+
+def stop_subprocess ( proc, kill_timeout_cs=10 ):
+   """Terminates or kills a subprocess created by subprocess.Popen().
+
+   Sends SIGTERM first and sends SIGKILL if the process is still alive after
+   the given timeout.
+
+   Returns: None
+
+   arguments:
+   * proc            -- subprocess
+   * kill_timeout_cs -- max time to wait after terminate() before sending a
+                        kill signal (in centiseconds). Should be an int.
+                        Defaults to 10 (= 1s).
+   """
+   if not subproc_send_term ( proc ):
+      return
+
+   try:
+      for k in range ( kill_timeout_cs ):
+         if proc.poll() is not None:
+            return
+         time.sleep ( 0.1 )
+   except:
+      subproc_send_kill ( proc )
+   else:
+      subproc_send_kill ( proc )
+# --- end of stop_subprocess (...) ---
+
+def gracefully_stop_subprocess ( proc, **kill_kwargs ):
+   try:
+      if subproc_send_term ( proc ):
+         proc.communicate()
+   except:
+      stop_subprocess ( proc, **kwargs )
+      raise
+
+def create_subprocess ( cmdv, **kwargs ):
+   """subprocess.Popen() wrapper that redirects stdin/stdout/stderr to
+   devnull or to a pipe if set to False/True.
+
+   Returns: subprocess
+
+   arguments:
+   * cmdv     --
+   * **kwargs --
+   """
+   devnull_obj = None
+
+   for key in { 'stdin', 'stdout', 'stderr' }:
+      if key not in kwargs:
+         pass
+      elif kwargs [key] is True:
+         kwargs [key] = subprocess.PIPE
+
+      elif kwargs [key] is False:
+         if devnull_obj is None:
+            devnull_obj = get_subproc_devnull()
+            assert devnull_obj is not None
+
+         kwargs [key] = devnull_obj
+      # else don't care
+   # --
+
+   return subprocess.Popen ( cmdv, **kwargs )
+# --- end of create_subprocess (...) ---
+
+def run_subprocess ( cmdv, kill_timeout_cs=10, **kwargs ):
+   """Calls create_subprocess() and waits for the process to exit.
+   Catches exceptions and terminates/kills the process in that case
+
+   Returns: 2-tuple ( subprocess, output )
+
+   arguments:
+   * cmdv            --
+   * kill_timeout_cs -- time to wait after SIGTERM before sending SIGKILL
+                        (in centiseconds), see stop_subprocess() for details
+                        Defaults to 10.
+   * **kwargs        --
+   """
+   proc = None
+   try:
+      proc   = create_subprocess ( cmdv, **kwargs )
+      output = proc.communicate()
+   except:
+      if proc is not None:
+         stop_subprocess ( proc, kill_timeout_cs=kill_timeout_cs )
+      raise
+
+   return ( proc, output )
+# --- end of run_subprocess (...) ---

Reply via email to