Index: lib/python/script/core.py
===================================================================
--- lib/python/script/core.py	(revision 60832)
+++ lib/python/script/core.py	(working copy)
@@ -47,6 +47,19 @@
 STDOUT = subprocess.STDOUT
 
 
+class CalledCommandError(subprocess.CalledProcessError):
+    def __init__(self, returncode, cmd, errors=None):
+        super(subprocess.CalledProcessError,
+              self).__init__(returncode=returncode, cmd=cmd)
+        self.errors = errors
+
+    def __str__(self):
+        # TODO: should this be translatable?
+        return _("Command '{cmd}' returned non-zero exit status {rc}"
+                 " and the following errors.\n"
+                 "%{errors}").format(cmd=self.cmd, rc=self.returncode,
+                                    errors=self.errors)
+
 class ScriptError(Exception):
     def __init__(self, msg):
         self.value = msg
@@ -344,8 +357,20 @@
 
     @return exit code (0 for success)
     """
-    ps = start_command(*args, **kwargs)
-    return ps.wait()
+    if 'stdin' not in kwargs:
+        raise ValueError('stdin argument is required.')
+    else:
+        stdin = kwargs['stdin']
+        del kwargs['stdin']  # clean non-standard argument
+    process = feed_command(*args, **kwargs)
+    unused, errors = process.communicate(stdin)
+    returncode = process.poll()
+    if returncode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = args[0]
+        raise CalledCommandError(returncode, cmd, errors=errors)
+    return 0
 
 
 def pipe_command(*args, **kwargs):
@@ -371,6 +396,8 @@
 
     @return Popen object
     """
+    if 'stdout' in kwargs:
+        raise ValueError('stdout argument not allowed, it would be overridden.')
     kwargs['stdout'] = PIPE
     return start_command(*args, **kwargs)
 
@@ -384,23 +411,75 @@
 
     @return Popen object
     """
+    if 'stdin' in kwargs:
+        raise ValueError('stdin argument not allowed, it would be overridden.')
     kwargs['stdin'] = PIPE
     return start_command(*args, **kwargs)
 
 
 def read_command(*args, **kwargs):
-    """!Passes all arguments to pipe_command, then waits for the process to
-    complete, returning its stdout (i.e. similar to shell `backticks`).
+    """Run command and return its output.
 
+    Passes all arguments to `pipe_command`, then waits for the process to
+    complete, returning its stdout (i.e., similar to shell backticks syntax).
+
     @param args list of unnamed arguments (see start_command() for details)
     @param kwargs list of named arguments (see start_command() for details)
 
     @return stdout
+
+    :raises CalledCommandError: if return code is non-zero
     """
-    ps = pipe_command(*args, **kwargs)
-    return ps.communicate()[0]
+    # implemenation inspired by subprocess.check_output() function
+    if 'stderr' in kwargs:
+        raise ValueError('stderr argument not allowed, it would be overridden.')
+    kwargs['stderr'] = PIPE
+    process = pipe_command(*args, **kwargs)
+    output, errors = process.communicate()
+    returncode = process.poll()
+    if returncode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = args[0]
+        raise CalledCommandError(returncode, cmd, errors=errors)
+    return output
 
 
+def write_read_command(*args, **kwargs):
+    """Run command and return its output.
+
+    Passes all arguments to `pipe_command`, then waits for the process to
+    complete, returning its stdout (i.e., similar to shell backticks syntax).
+
+    @param args list of unnamed arguments (see start_command() for details)
+    @param kwargs list of named arguments (see start_command() for details)
+
+    @return stdout
+
+    :raises CalledCommandError: if return code is non-zero
+    """
+    # implemenation inspired by subprocess.check_output() function
+    if 'stdin' not in kwargs:
+        raise ValueError('stdin argument is required.')
+    if 'stdout' in kwargs:
+        raise ValueError('stdout argument not allowed, it would be overridden.')
+    if 'stderr' in kwargs:
+        raise ValueError('stderr argument not allowed, it would be overridden.')
+    stdin = kwargs['stdin']  # get a string for stdin
+    kwargs['stdin'] = PIPE  # to be able to send data to stdin
+    kwargs['stdout'] = PIPE
+    kwargs['stderr'] = PIPE
+    process = start_command(*args, **kwargs)
+    output, errors = process.communicate(stdin)
+    returncode = process.poll()
+    if returncode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = args[0]
+        raise CalledCommandError(returncode, cmd, errors=errors)
+    return output
+
+
 def parse_command(*args, **kwargs):
     """!Passes all arguments to read_command, then parses the output
     by parse_key_val().
@@ -422,6 +501,8 @@
     @param kwargs list of named arguments (see start_command() for details)
 
     @return parsed module output
+
+    :raises CalledCommandError: if return code is non-zero
     """
     parse = None
     parse_args = {}
@@ -452,11 +533,24 @@
 
     @return return code
     """
-    stdin = kwargs['stdin']
-    p = feed_command(*args, **kwargs)
-    p.stdin.write(stdin)
-    p.stdin.close()
-    return p.wait()
+    # implemenation inspired by subprocess.check_output() function
+    if 'stdin' not in kwargs:
+        raise ValueError('stdin argument is required.')
+    if 'stderr' in kwargs:
+        raise ValueError('stderr argument not allowed, it would be overridden.')
+    stdin = kwargs['stdin']  # get a string for stdin
+    kwargs['stdin'] = PIPE  # to be able to send data to stdin
+    kwargs['stderr'] = PIPE
+    process = start_command(*args, **kwargs)
+    unused, errors = process.communicate(stdin)
+    returncode = process.poll()
+    if returncode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = args[0]
+        raise CalledCommandError(returncode, cmd, errors=errors)
+    # subprocess.check_call() function returns 0 for success
+    # but this would create only confusion since we don't return for failure
 
 
 def exec_command(prog, flags="", overwrite=False, quiet=False, verbose=False,
