25 March 2015

I believe every veteran has their own set of tools, including some code snippets they always carry with them. The python code to call external commands is one of this kind of code snippets to me.

Although calling external commands is a snap in python 2.7, I still have to deal with running commands in python 2.6 (or even 2.4). In addition, my scripts often need to run commands remotely (via ssh). Below are wrappers that I uses.

For python 2.6

I define an exception to raise when an error happens. Some dislike this idea, but I love it because I am a fan of "exception" error handling mechanism.

import logging
import subprocess

# for python2.6
def run_cmd(cmd, input_str=None):
    # mimic subprocess.check_output, which is available in python2.7
    logging.debug('run cmd: %s, input: "%s"', cmd, input_str)
    process = subprocess.Popen(cmd,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    output, _ = process.communicate(input_str)
    retcode = process.poll()

    if retcode:
        raise CalledProcessError(retcode, cmd, output)

    return output

def ssh_run(host, cmd, extra_ssh_options=None):
    if extra_ssh_options is None:
        extra_ssh_options = []

    return run_cmd(['ssh',
                    '-o', 'LogLevel=QUIET',
                    '-o', 'BatchMode=yes',
                    '-o', 'StrictHostKeyChecking=no',
                    '-o', 'ConnectionAttempts=2',
                    '-o', 'ConnectTimeout=2']
                   + extra_ssh_options
                   + [host, cmd])

class CalledProcessError(Exception):
    '''To overwrite the official one, which, in python2.6 does not support
    "output"

    '''

    def __init__(self, returncode, cmd, output=None):
        super(CalledProcessError, self).__init__()
        self.returncode = returncode
        self.cmd = cmd
        self.output = output

    def __str__(self):
        return "[Errno %d] %s" % (self.returncode, self.output)

For python 2.4

For even older version python, the run_cmd have to make use of "commands" instead of "subprocess".

import commands

def run_cmd(cmd):
    '''Call this function to hide changes between Python 2.4 and 2.7.

    '''
    (status, output) = commands.getstatusoutput(cmd)

    if status != 0:
        raise CalledProcessError(status, cmd, output)

    return output


blog comments powered by Disqus