This may not be the best way to do this, but it works (at least on my machine):
import os
import sys
import unittest
STDOUT_FD = os.dup(sys.stdout.fileno())
STDERR_FD = os.dup(sys.stderr.fileno())
with open('stdout.txt', 'w') as f, open('stderr.txt', 'w') as g:
os.dup2(f.fileno(), sys.stdout.fileno())
os.dup2(g.fileno(), sys.stderr.fileno())
class MyTest(unittest.TestCase):
def test_print(self):
print 'some output'
self.assertEqual('', '')
def test_some_moar(self):
print 'some other cool output'
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()
print 'I am printing to stdout.txt'
print >> sys.stderr, 'I am printing to stderr.txt'
# revert the File Descriptors
os.dup2(STDOUT_FD, sys.stdout.fileno())
os.dup2(STDERR_FD, sys.stderr.fileno())
print "Yay! Back to printing in the console"
Running this produces:
stdout.txt
some output
some other cool output
stderr.txt
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
os.dup(fd)
The os.dup
function creates a copy of the file descriptor and returns the integer of that duplicated file descriptor. So, after the first two dup
calls, there are two file descriptors pointing to stdout
and two pointing to stderr
.
os.dup2(fd, fd2)
The os.dup2
function copies the file descriptor from fd
to fd2
and closes the file descriptor of fd2
. So, after the dup2
calls, stdout
now points to the f
file descriptor and likewise stderr
now points to the g
file descriptor (and because dup2
closes the second file descriptor, there is only a single file descriptor for both stdout
and stderr
because of the copies made by the call to dup
).
Print everything to your heart's content.
At the end, the last two dup2
calls revert the file descriptors using the copied file descriptors (so stdout and stderr point to where you'd expect) which also closes files f
and g
.
According to the dup2 docs, this works on both Linux and Windows.
[Edit]
If it's not too much work, I would suggest not using prints and use logging
instead:
import logging
import unittest
class MyTest(unittest.TestCase):
def test_print(self):
logging.info('some output')
self.assertEqual('', '')
def test_some_moar(self):
logging.info('some other cool output')
self.assertTrue(True)
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
# get the default logger
logger = logging.getLogger()
# add a file handler
logger.addHandler(logging.FileHandler('stdout.txt', mode='w'))
# set up a stream for all stderr output
stderr_file = open('stderr.txt', 'w')
# attach that stream to the testRunner
unittest.main(testRunner=unittest.TextTestRunner(stream=stderr_file))