[master 4/4] Simplify iutil.execReadlines

David Shea dshea at redhat.com
Fri Aug 8 19:32:50 UTC 2014


Remove the thread and queue between execReadlines and the process it
starts. Share the process starting code between execReadlines and
_run_program.
---
 pyanaconda/iutil.py | 101 +++++++++++++++++++++-------------------------------
 1 file changed, 40 insertions(+), 61 deletions(-)

diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index 29aff76..1334f24 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -30,8 +30,6 @@ import unicodedata
 import string
 import types
 import re
-from threading import Thread
-from Queue import Queue, Empty
 from urllib import quote, unquote
 
 from pyanaconda.flags import flags
@@ -97,16 +95,15 @@ def setSysroot(path):
     global _sysroot
     _sysroot = path
 
-def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_output=True, binary_output=False):
-    """ Run an external program, log the output and return it to the caller
+def _start_program(argv, root='/', stdin=None, env_prune=None):
+    """ Start an external program and return the Popen object.
+        stdout is set to PIPE, stderr is redirected to stdout.
+
         :param argv: The command to run and argument
         :param root: The directory to chroot to before running command.
         :param stdin: The file object to read stdin from.
-        :param stdout: Optional file object to write stdout and stderr to.
         :param env_prune: environment variable to remove before execution
-        :param log_output: whether to log the output of command
-        :param binary_output: whether to treat the output of command as binary data
-        :return: The return code of the command and the output
+        :return: A Popen object for the running command.
     """
     if env_prune is None:
         env_prune = []
@@ -125,17 +122,31 @@ def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_ou
     with program_log_lock:
         program_log.info("Running... %s", " ".join(argv))
 
-        env = augmentEnv()
-        for var in env_prune:
-            env.pop(var, None)
+    env = augmentEnv()
+    for var in env_prune:
+        env.pop(var, None)
 
-        try:
-            proc = subprocess.Popen(argv,
-                                    stdin=stdin,
-                                    stdout=subprocess.PIPE,
-                                    stderr=subprocess.STDOUT,
-                                    preexec_fn=chroot, cwd=root, env=env)
+    return subprocess.Popen(argv,
+                            stdin=stdin,
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT,
+                            preexec_fn=chroot, cwd=root, env=env)
 
+def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_output=True, binary_output=False):
+    """ Run an external program, log the output and return it to the caller
+        :param argv: The command to run and argument
+        :param root: The directory to chroot to before running command.
+        :param stdin: The file object to read stdin from.
+        :param stdout: Optional file object to write stdout and stderr to.
+        :param env_prune: environment variable to remove before execution
+        :param log_output: whether to log the output of command
+        :param binary_output: whether to treat the output of command as binary data
+        :return: The return code of the command and the output
+    """
+    try:
+        proc = _start_program(argv, root, stdin, env_prune)
+
+        with program_log_lock:
             output_string = proc.communicate()[0]
             if output_string:
                 if binary_output:
@@ -152,10 +163,12 @@ def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_ou
                     if stdout:
                         stdout.write(line)
 
-        except OSError as e:
+    except OSError as e:
+        with program_log_lock:
             program_log.error("Error running %s: %s", argv[0], e.strerror)
-            raise
+        raise
 
+    with program_log_lock:
         program_log.debug("Return code: %d", proc.returncode)
 
     return (proc.returncode, output_string)
@@ -224,55 +237,21 @@ def execReadlines(command, argv, stdin=None, root='/', env_prune=None):
         Output from the file is not logged to program.log
         This returns a generator with the lines from the command until it has finished
     """
-    if env_prune is None:
-        env_prune = []
-
-    # Return the lines from stdout via a Queue
-    def queue_lines(out, queue):
-        for line in iter(out.readline, b''):
-            queue.put(line.strip())
-        out.close()
-
-    def chroot():
-        if root and root != '/':
-            os.chroot(root)
-            os.chdir("/")
-
     argv = [command] + argv
-    with program_log_lock:
-        program_log.info("Running... %s", " ".join(argv))
 
-    env = augmentEnv()
-    for var in env_prune:
-        env.pop(var, None)
     try:
-        proc = subprocess.Popen(argv,
-                                stdin=stdin,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.STDOUT,
-                                bufsize=1,
-                                preexec_fn=chroot, cwd=root, env=env)
+        proc = _start_program(argv, root, stdin, env_prune)
     except OSError as e:
-        program_log.error("Error running %s: %s", argv[0], e.strerror)
+        with program_log_lock:
+            program_log.error("Error running %s: %s", argv[0], e.strerror)
         raise
 
-    q = Queue()
-    t = Thread(target=queue_lines, args=(proc.stdout, q))
-    t.daemon = True # thread dies with the program
-    t.start()
-
-    while True:
-        try:
-            line = q.get(timeout=.1)
-            yield line
-            q.task_done()
-        except Empty:
-            if proc.poll() is not None:
-                if os.WIFSIGNALED(proc.returncode):
-                    raise OSError("process '%s' was killed" % argv)
-                break
-    q.join()
+    for line in iter(proc.stdout.readline, b''):
+        yield line.strip()
 
+    if proc.poll() is not None:
+        if os.WIFSIGNALED(proc.returncode):
+            raise OSError("process '%s' was killed" % argv)
 
 ## Run a shell.
 def execConsole():
-- 
1.9.3



More information about the anaconda-patches mailing list