18

Since the commands module is deprecated since Python 2.6, I'm looking into the best way to replace commands.getstatusoutput which returns a tuple of the command's return code and output. The subprocess module is rather obvious, however, it doesn't offer a direct replacement for getstatusoutput. A potential solution is discussed in a related question concerning getstatusoutput - however, I'm not looking into rewriting the original function (which has less then 10 LOC anyway) but would like to know if there is a more straightforward way.

Community
  • 1
  • 1
  • Did you read the answer to that question? The Pythonic solution is to use `Popen.communicate`, then read the `Popen.statuscode`. That will give you the stdout and stderr in separate strings, though. – Fred Foo Jul 05 '12 at 12:47
  • 1
    Yes, but as I said, I'm not looking into rewriting the commands module's function. What the original function does is to simply use os.popen, then read and close the pipe. That's what the suggested new version does as well (once fixed w/ what's written in the answer). However, I was hoping for anything resembling that function in the standard library w/out having to maintain my own replacement. – Gerald Senarclens de Grancy Jul 05 '12 at 13:08

3 Answers3

19

There is no direct replacement, because commands.getstatusoutput was a bad API; it combines stderr and stdout without giving an option to retrieve them separately.

The convenience API that you should be using is subprocess.check_output as it will throw an exception if the command fails.

Otherwise, it does appear somewhat of a deficiency that subprocess doesn't provide a method to retrieve output and status in a single call, but it's easy to work around; here's what the answer to the linked question should have been:

def get_status_output(*args, **kwargs):
    p = subprocess.Popen(*args, **kwargs)
    stdout, stderr = p.communicate()
    return p.returncode, stdout, stderr

If you want stdout and stderr together, use stderr=subprocess.STDOUT.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
10

getstatusoutput is back (from python 3.1) :) See: http://docs.python.org/3.3/library/subprocess.html#legacy-shell-invocation-functions

Liso
  • 2,165
  • 1
  • 16
  • 19
0

To answer the question from the title: here's asyncio-based getstatusoutput() implementation -- it is code example from the docs that is modified to follow more closely subprocess.getstatusoutput() interface:

import asyncio
import locale
from asyncio.subprocess import PIPE, STDOUT

@asyncio.coroutine
def getstatusoutput(cmd):
    proc = yield from asyncio.create_subprocess_shell(cmd,
            stdout=PIPE, stderr=STDOUT)
    try:
        stdout, _ = yield from proc.communicate()
    except:
        try:
            proc.kill()
        except ProcessLookupError: # process is already dead
            pass
        raise
    finally:
        exitcode = yield from proc.wait()
    # return text
    output = stdout.decode(locale.getpreferredencoding(False))
    # universal newlines mode
    output = output.replace("\r\n", "\n").replace("\r", "\n")
    if output[-1:] == "\n": # remove a trailing newline
        output = output[:-1]
    return (exitcode, output)

It works both Windows and Unix. To run it (from the same example):

import os
from contextlib import closing

if os.name == 'nt': # Windows
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
with closing(loop):
    coro = getstatusoutput('python -m platform')
    exitcode, stdout = loop.run_until_complete(coro)
    if exitcode == 0:
        print("Platform:", stdout)
    else:
        print("Python failed with exit code %s: %s" % (exitcode, stdout))
jfs
  • 399,953
  • 195
  • 994
  • 1,670