I'm learning Python these days while writing an script to automate the testing of ATF under multiple virtual machines. I had this code in a shell script, but it is so ugly and clumsy that I don't even dare to add it to the repository. Hopefully, the new version in Python will be more robust and versatile enough to be published.

One of the things I've been impressed by is the subprocess module and, in special, its Popen class. By using this class, it is trivial to spawn subprocesses and perform some IPC with them. Unfortunately, Popen does not provide any way to silence the output of the children. As I see it, it'd be nice if you'd pass an IGNORE flag as the stdout/stderr behavior, much like you can currently set those to PIPE or set stderr to STDOUT.

The following trivial module implements this idea. It extends Popen so that the callers can pass the IGNORE value to the stdout/stderr arguments. (Yes, it is trivial but it is also one of the first Python code I write so... it may contain obviously non-Pythonic, ugly things.) The idea is that this exposes the same interface so that it can be used as a drop-in replacement. OK, OK, it lacks some methods and the constructor does not match the original signature, but this is enough for my current use cases!

import subprocess


IGNORE = -3
STDOUT = subprocess.STDOUT
assert IGNORE != STDOUT, "IGNORE constant is invalid"


class Popen(subprocess.Popen):
"""Extension of subprocess.Popen with built-in support
for silencing the output channels of a child process"""

__null = None

def __init__(self, args, stdout = None, stderr = None):
subprocess.Popen.__init__(self, args = args,
stdout = self._channel(stdout),
stderr = self._channel(stderr))

def __del__(self):
self._close_null()

def wait(self):
r = subprocess.Popen.wait(self)
self._close_null()
return r

def _null_instance(self):
if self.__null == None:
self.__null = open("/dev/null", "w")
return self.__null

def _close_null(self):
if self.__null != None:
self.__null.close()
self.__null = None
assert self.__null == None,
"Inconsistent internal state"

def _channel(self, behavior):
if behavior == IGNORE:
return self._null_instance()
else:
return behavior
By the way, somebody else suggested this same thing a while ago. Don't know why it hasn't been implemented in the mainstream subprocess module.

Comments from the original Blogger-hosted post: